0% menganggap dokumen ini bermanfaat (0 suara)
127 tayangan

Java Script Lanjutan

1. Tulisan ini membahas beberapa alat bantu (tools) yang berguna untuk mengembangkan aplikasi web modern seperti Node.js, Gulp, dan alat penguji permintaan HTTP. 2. Gulp digunakan sebagai alat bantu otomatisasi manajemen file yang dapat menggabungkan dan meminimalkan file-file Javascript dan CSS. 3. Alat penguji permintaan HTTP memudahkan pengujian komunikasi dengan server.

Diunggah oleh

kind3rn
Hak Cipta
© © All Rights Reserved
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd
0% menganggap dokumen ini bermanfaat (0 suara)
127 tayangan

Java Script Lanjutan

1. Tulisan ini membahas beberapa alat bantu (tools) yang berguna untuk mengembangkan aplikasi web modern seperti Node.js, Gulp, dan alat penguji permintaan HTTP. 2. Gulp digunakan sebagai alat bantu otomatisasi manajemen file yang dapat menggabungkan dan meminimalkan file-file Javascript dan CSS. 3. Alat penguji permintaan HTTP memudahkan pengujian komunikasi dengan server.

Diunggah oleh

kind3rn
Hak Cipta
© © All Rights Reserved
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd
Anda di halaman 1/ 97

Pengenalan

Pengembangan web modern melibatkan sangat banyak bahasa pemrograman dan


sistem. Pada sisi klien (front end) saja misalnya, secara standar kita
memerlukan tiga bahasa pemrograman untuk membangun antarmuka aplikasi: HTML,
CSS, dan Javascript. Masalah yang biasanya muncul ketika mengembangkan
aplikasi seperti ini adalah manajemen file. Ketika mengembangkan aplikasi
besar, ledakan jumlah file HTML, CSS, dan Javascript tidak dapat dihindari.
Tidak cukup sampai di sana, berdasarkan best practice yang dikenal luas

[1]

idealnya kita harus meminimalkan ukuran file dan menggabungkan masing-masing


jenis file (Javascript dan CSS) menjadi 1 file saja. Kalau pada bahasa
pemrograman lain terdapat compileratau build tools yang membantu kita
melakukan hal ini, Javascript (maupun CSS dan HTML) tidak memiliki
dukungan toolsserupa secara standar.
Untungnya, para pengembang Javascript sendiri menyadari hal ini dan telah
mengembangkan berbagai tools untuk membantu kita dalam melakukan manajemen
file maupun hal-hal lainnya yang mempercepat pengembangan. Bagian ini akan
membahas beberapa tools yang dapat digunakan untuk hal ini, beserta cara
instalasi dan penggunaannya.
NodeJS
Sebelum mulai menggunakan tools yang diberikan, kita akan terlebih dahulu
melakukan instalasi NodeJS. NodeJS adalah sebuah platform pengembangan
perangkat lunak Javascript yang berjalan di luar browser. Sepintas NodeJS
memiliki manfaat yang sama seperti Java Runtime Environment (JRE), yang
merupakan tempat berjalannya aplikasi yang dibangun dengan bahasa pemrograman
Java. Jika JRE dibutuhkan untuk menjalankan aplikasi Java, maka NodeJS
dibutuhkan untuk menjalankan aplikasi Javascript yang dibangun untuk NodeJS.
Langsung saja, berikut adalah langkah demi langkah instalasi NodeJS pada
sistem operasi Windows:
1. Ambil perangkat lunak NodeJS dari website resminya
di http://nodejs.org/, melalui tombol install yang disediakan.

2. Jalankan installer yang baru didapatkan, untuk melihat tampilan seperti


gambar di bawah, dan tekan tombol Next.

3. Pastikan checkbox I accept the terms in the Lincense Agreement telah


di cek, kemudian tekan Next.

4. Tentukan lokasi instalasi (disarankan untuk tidak mengubah apa yang


disediakan sistem di sini), kemudian tekan Next.

5. Pada langkah selanjutnya, kita tidak perlu melakukan apa-apa, tetapi


pastikan bagian Add to PATH terpilih dengan benar (lihat bagian yang
diberi warna merah pada gambar). Tekan Next.

6. Tunggu proses instalasi hingga selsai.

7. Setelah proses selesai, tekan Finish.

8. Jalankan perintah node --version pada CMD. Jika NodeJS terpasang dengan
benar, sistem akan mengembalikan nomor versi.

Instalasi NodeJS juga menyertakan sebuah perangkat lunak lain,


yaitu npm. npm merupakan perangkat lunak manajemen paket, yang membantu kita
dalam melakukan instalasi library maupun perangkat lunak lain yang berjalan
di atas NodeJS. Berbagai perangkat lunak pendukung yang akan kita gunakan
nanti akan diinstalasi dengan menggunakan npm.
Build Tools
Build tools merupakan perangkat lunak yang membantu kita dalam melakukan
otomasi manajemen file. Biasanya kita hanya perlu memberitahukan kepada build
tools file mana yang ingin dikompilasi, dipindahkan, dan sejenisnya. Setelah
memberitahukan bagaimana manajemen file yang kita inginkan, kita cukup
memanggil build tools untuk menjalankan tugas-tugas tersebut secara otomatis.
Terdapat sangat banyak build tools pada platform NodeJS,
contohnya: GruntJS, Mimosa, Gulp, dan lainnya. Pada tulisan ini kita hanya
akan menggunakan Gulp saja, karena pembahasan akan keseluruhan build
tools tidak akan memungkinkan.
Instalasi Gulp dapat dilakukan dengan menjalankan perintah npm install gulp:

Untuk mempermudah dalam mempelajari penggunaan Gulp, kita akan melakukan


studi kasus. Misalkan kita sedang mengembangkan sebuah website sederhana
dengan struktur file seperti berikut:

Asumsikan kita ingin menggabungkan semua file Javascript yang ada dalam
direktori scripts serta file CSS yang ada dalam direktori styles menjadi
satu. File Javascript akan kita gabungkan menjadi file scripts.js dan
disimpan dalam direktori js. File CSS akan digabung menjadi
file styles.css dan disimpan dalam direktori css.
Untuk memberitahukan Gulp apa yang akan kita lakukan, kita harus membuat
sebuah file khusus yang dikenali Gulp terlebih dahulu. File ini harus
bernama gulpfile.js, dan harus diletakkan di direktori utama proyek. Dalam
kasus ini, kita harus membuat gulpfile.js di dalam direktori
tempat index.html berada. Isi dari gulpfile.js sangat sederhana, hanya
melakukan inisialisasi data:

var gulp = require('gulp');

2
3

gulp.task('default', function () {

4
5

// masukkan perintah gulp di sini


});

Terdapat lima perintah utama yang dapat kita berikan kepada Gulp, yaitu:
1. gulp.task(name, fn): yang telah kita gunakan pada kode di atas. Membuat
perintah baru untuk Gulp ketika dipanggil melalui command
line nantinya. Parameter name merupakan nama dari task yang dibuat,
sementara fnadalah fungsi yang dijalankan ketika task dipanggil.
2. gulp.run(tasks...): menjalankan satu atau lebih task yang dibuat
menggunakan gulp.task melalui kode (programmatically).
3. gulp.watch(glob, fn): melakukan eksekusi fungsi fn ketika file yang
dispesifikasikan oleh glob berubah isinya.
4. gulp.src(glob): membaca file yang memenuhi syarat sesuai
spesifikasi glob dan kemudian mengembalikan file tersebut satu per satu
untuk kemudian diproses.
5. gulp.dest(folder): file yang dikirimkan ke perintah ini akan disimpan
di dalam folder.
Selain kelima perintah di atas, Gulp menyediakan satu perintah pendukung
lagi, yaitu pipe(fn). Perintah pipemerupakan saluran penghubung antar dua
perintah, yang pada dasarnya memberikan fasilitas untuk menjalankan satu
perintah setelah perintah sebelumnya dijalankan.
Agar lebih mudah pahami, kita akan langsung melihat contoh penggunaan
beberapa perintah di atas. Sebagai langkah pertama, katakan lah kita akan
melakukan kopi seluruh file Javascript dari scripts ke js. Ganti isi
file glupfile.jsmenjadi seperti berikut:
1
2
3
4

var gulp = require('gulp');

var scriptFiles = './scripts/**/*.js';

5
6

gulp.task('default', function () {
gulp.src(scriptFiles)

7
8

.pipe(gulp.dest('./js/'));
});

Pada kode di atas, kita menggunakan perintah gulp.src dan gulp.dest secara
berurutan. Kedua perintah ini dihubungkan oleh pipe. Untuk melakukan
eksekusi task di atas, jalankan perintah glup dari command line.

Bagaimana jika kita ingin menggabungkan file atau meminimalkan file? Untuk
berbagai tugas tambahan seperti itu, kita dapat menggunakan tambahan perintah
dari pihak ketiga. Jalankan perintah berikut untuk memasang perintah baru
dari pihak ketiga:
1

$ npm install gulp-concat gulp-uglify

Perintah di atas melakukan instalasi terhadap dua perintah dari pihak


ketiga: gulp-concat untuk menggabungkan file, dan gulp-uglify untuk
meminimalkan kode. Cara penggunaannya juga sangat sederhana:
1

var gulp

= require('gulp');

var concat = require('gulp-concat');

var uglify = require('gulp-uglify');

4
5

var scriptFiles = './scripts/**/*.js';

6
7

gulp.task('default', function () {

gulp.src(scriptFiles)

.pipe(concat('scripts.js'))

10

.pipe(uglify())

11

.pipe(gulp.dest('./js/'));

12

});

Cukup gamblang dan sederhana. Kita hanya melakukan impor modul, dan kemudian
menggunakan modul tersebut (fungsiconcat() dan uglify()) melalui pipe.
Kesederhanaan ini merupakan daya tarik utama dari Gulp, sebagai sebuah build
tools yang sederhana sekaligus sangat bermanfaat. Penggabungan dan minifikasi
file CSS diserahkan kepada pembaca sebagai latihan.
Penguji HTTP Request
Ketika kita mengembangkan aplikasi web dinamis, seringkali kita akan
berhubungan dengan server. Hubungan dengan server ini biasanya dilakukan
secara dinamis, dengan menggunakan teknologi seperti XMLHttpRequest atau
WebSocket. Untuk memudahkan kita dalam menguji apakah server memberikan data
yang tepat atau sesuai dengan yang kita inginkan, kita dapat
menggunakan tools yang dapat mengirimkan permintaan ke server, dengan format
yang kita inginkan.
Beberapa tools yang dapat digunakan untuk kepentingan ini yaitu:
1. Advanced REST Client (Chrome)
2. REST Client (Firefox)

Kedua tools di atas cukup sederhana dan mudah digunakan. Kita hanya perlu
memasukkan URL yang akan kita uji, beserta dengan method HTTP-nya. Program
kemudian akan menampilkan data yang diberikan dari server, dan kita dapat
langsung memastikan apakah format yang diberikan sudah benar atau tidak. Hal
ini akan sangat memudahkan kita ketika ingin berbicara degnan server.

Catatan Kaki

XMLHttpRequest dan AJAX


Apa itu AJAX?
AJAX merupakan singkatan dari Asynchronous Javascript and XML. Secara
sederhana, AJAX memungkinkan kita untuk berkomunikasi dengan kode program
pada server melalui Javascript. Melalui AJAX, kita dapat memanggil kode pada
server melalui URL tertentu, dan menerima data / pesan hasil eksekusi oleh
server. Pada awal pengembangan, server mengembalikan data dalam format XML
(X pada AJAX). Pada prakteknya, server dapat mengembalikan data apapun,
selaam kode klien mengetahui format yang diberikan. Bahkan pada perkembangan
selanjutnya, format JSON menjadi lebih populer dibandingkan XML.
Bagian yang paling menarik dari AJAX ialah bahwa komunikasi dengan server
dilakukan secara asinkron (A pada AJAX). Hal ini berarti kita dapat
berkomunikasi dengan server tanpa mengganggu aktivitas atau eksekusi lain
pada sisi klien maupun server. Kita bahkan tidak perlu berpindah halaman atau
melakukan reload halaman untuk melihat hasil eksekusi kode AJAX.
Jadi, dengan AJAX kita dapat melakukan dua hal berikut:
1. Mengirimkan perintah kepada server tanpa harus berpindah
atau reload halaman.

2. Menerima data dari server dan kemudian mengolahnya. Data hanya akan
dapat diberikan server setelah kita mengirimkan perintah.
Setelah kita menerima dan mengolah data dari server, kita kemudian dapat
memperbaharui halaman sesuai dengan kebutuhan. Ingat juga bahwa komunikasi
ke server pada teknologi AJAX berjalan satu arah: kita mengirimkan perintah
ke server, dan hanya setelah itu server dapat mengirimkan data kembali ke
klien. Jika tidak ada pengiriman perintah dari klien, maka server tidak dapat
langsung memberikan data ke klien. Hal ini disebabkan oleh sifat dasar dari
web yang*stateless*, di mana server tidak menyimpan daftar klien yang sedang
terkoneksi dengannya. Tanpa server mengetahi siapa saja yang sedang
terkoneksi dengannya, maka ia tidak dapat mengirimkan data kepada klien
tersebut.
Keseluruhan fungsi dari AJAX ini dapat dicapai dengan menggunakan
objek XMLHttpRequest yang telah disediakan oleh browser. Langsung saja, kita
akan melihat bagaimana menggunakan objek ini.
Pembuatan XMLHttpRequest
Objek XMLHttpRequest (selanjutnya dirujuk sebagai xhr saja dalam kode dan
nama method) merupakan objek inti untuk melakukan operasi AJAX. Pembuatan
objek ini pada browser modern cukup mudah dan jelas:
1

var xhr;

if (window.XMLHttpRequest) {

3
4

xhr = new XMLHttpRequest();


} else {

5
6

alert("Browser tidak mendukung AJAX");


}

Sayangnya, kode di atas tidak dapat digunakan jika kita harus


mendukung browser lama seperti IE6. Jika ingin menggunakan operasi AJAX
pada browser lama, kita dapat menggunakan objek alternatif,
yaitu ActiveXObject:
1
2

var xhr;

if (window.XMLHttpRequest) { // Chrome, safari, IE7+, Opera, dll

xhr = new XMLHttpRequest();

} else if (window.ActiveXObject) { // IE5/6

xhr = new ActiveXObject("Microsoft.XMLHTTP");

} else {

alert("Browser tidak mendukung AJAX!");

Cara kedua yang mendukung browser lama ini tidak disarankan untuk digunakan,
karena dapat menambah beban pengujian. Anda lebih disarankan untuk
memperbaharui browser anda dibandingkan dengan menggunakan kode di atas.
Menggunakan browser lama yang tidak didukung lagi membuka celah kemanan yang
sangat besar, ditambah lagiActiveXObject merupakan objek yang tidak didukung
oleh standar Javascript secara resmi.
Mengirimkan Perintah ke Server
Mengirimkan perintah ke server dilakukan melalui protokol HTTP, yang dapat
dipanggil menggunakan method xhr.opendan xhr.send dari XMLHttpRequest:
1

xhr.open("GET", "http://example.com/ajax/", true);

xhr.send(null);

Method xhr.open digunakan untuk melakukan inisialisasi perintah yang akan


kita kirimkan ke server. Parameter yang kita kirimkan ke xhr.open memiliki
arti tersendiri:

Parameter pertama dari xhr.open menentukan HTTP Request Method yang


akan dikirimkan ke server. Terdapat total 9 method yang dapat
dikirimkan, tetapi yang paling umum digunakan adalah GET, POST, PUT,
dan DELETE. Ingat bahwa server juga harus dapat mengerti method yang
dikirimkan oleh klien. Nama method juga harus dituliskan dalam huruf
kapital.

Parameter kedua dari xhr.open berisi alamat URL server yang dapat
menerima dan mengerti perintah yang kita kirimkan. Untuk menjaga
keamanan, kita tidak dapat mengirimkan perintah ke domain yang berbeda

(misal: dari example.com ke contoh.com). Hal ini dibuat untuk


menghindari pencurian data pengguna ketika perantas berhasil menanamkan
kode Javascript mereka ke dalam halaman web kita. Jika memang perlu
mengirimkan perintah ke domain lain, baca HTTP Access Control (CORS)

Parameter ketiga dari xhr.open menentukan apakah perintah yang


dikirimkan bersifat asinkron atau tidak. Parameter ini bersifat
opsional, dan jika tidak diisi akan bernilai true. Dalam kebanyakan
kasus, parameter ini tidak perlu diisikan.

Setelah selesai melakukan inisialisasi melalui xhr.open, kita kemudian dapat


mengirimkan perintah ke server dengan menggunakan method xhr.send.
Method xhr.send menerima satu parameter yang bersifat opsional. Parameter ini
berisi data yang akan kita kirimkan ke server. Data yang dikirimkan dapat
disimpan dalam beberapa jenis objek,
yaituFormData, Blob, ArrayBufferView, Document, dan DOMString. Jika tidak
mengirimkan data ke server, kita dapat mengosongkan parameter ini, atau
mengisikannya dengan nilai null.
Untuk dapat mengirimkan data ke server, tentunya kita juga harus mengetahui
format pengiriman data yang dikenal oleh server pada HTML. Misalnya,
untuk method GET pada HTML, data dikirimkan secara otomatis melalui Query
String dalam format seperti berikut:
1

http://example.com/path?param1=value1&param2=value2...

Karena bentuk format data yang sederhana ini kita dapat langsung mengirimkan
data melalui URL pada server, pada method xhr.open seperti berikut:
1

xhr.open("GET", "http://example.com/path?param1=value1&param2=value2", true);

xhr.send(null);

Sayangnya, hal ini tidak dapat kita gunakan pada pengiriman data
menggunakan method POST. Jika ingin mengirimkan data menggunakan method POST,
kita harus terlebih dahulu menyimpan data yang ada di dalam objek yang dapat
diterima oleh xhr.send dan mengirimkannya melalui xhr.send. Berikut adalah
contoh penggunaan objek FormDatauntuk mengambil data dalam form dan
mengirimkannya melalui POST:

var form

= document.querySelector("#form-login");

var formData = new FormData(form);

3
4

xhr.open("POST", "http://example.com/login");

xhr.send(formData);

Kita bahkan dapat menambahkan data baru ke FormData jika diperlukan (misalnya
untuk data yang tidak perlu diisikan oleh pengguna seperti nomor
pendaftaran). Kita dapat menggunakan method FormData.append untuk ini:
1

var form

= document.querySelector("#form-daftar");

var formData = new FormData(form);

3
4

formData.append("NomorPendaftaran", noDaftar++);

5
6

xhr.open("PSOT", "http://example.com/register");

xhr.send(formData);

Seperti yang dapat dilihat, FormData.append menerima dua buah parameter:


parameter pertama berisikan nama data yang dikirimkan, dan parameter kedua
berisi nilai (isi) dari datanya.
Menangani Respon Server
Setelah dapat mengirimkan data dan perintah ke server, langkah selanjutnya
dari pemrograman AJAX adalah menerima (dan menangani) respon dari server akan
perintah kita. Untuk dapat melakukan hal ini, kita harus membuat sebuah
fungsi baru dan mengikatkannya pada xhr.onreadystatechange, seperti berikut:
1

var fungsiRespon = function () {

2
3

// kode...
};

4
5

xhr.onreadystatechange = fungsiRespon;

Method xhr.onreadystatechange merupakan method yang dijalankan ketika


atribut xhr.readyState dari objekXMLHttpRequest berubah nilainya.
Atribut xhr.readyState menyimpan penanda status dari request yang kita
kirimkan ke server. Terdapat lima nilai xhr.readyState yang dimungkinkan,
yaitu:
Nilai

State

Deskripsi

UNSENT

xhr.open belum dipanggil.

OPENED

xhr.open telah dipanggil, tetapi xhr.send belum.

HEADERS_RECEIVED

xhr.send telah dipanggil, dan server telah


mengembalikan header dan status HTTP.

LOADING

Sedang mengambil data respon dari server.

DONE

Seluruh operasi telah selesai.

Karena kita ingin mengambil respon dari server, maka di


dalam fungsiRespon kita perlu melakukan cek status darixhr.readyState dan
kemudian melakukan sesuatu jika respon server sudah didapatkna (operasi sudah
selesai). Status dapat kita akses langsung seperti berikut:
1

var fungsiRespon = function () {

if (xhr.readystate === 4) {

// respon dari server sudah diterima

4
5

}
}

Setelah memastikan server memberikan respon sepenuhnya, kita lalu masih harus
memastikan server tidak mengembalikan pesan kesalahan. Status keberhasilan
atau kesalahan pada server HTTP dikomunikasikan kepada client melalui HTTP
Status Code. Contohnya, jika server sukses menjalankan perintah tanpa masalah
kita akan diberi kode 200. Jika halaman tidak ditemukan maka kita akan

diberikan status code 404. Dengan pengetahuan ini, kita dapat melakukan
pengecekan tambahan, untuk memastikan server menjalankan perintah dengan
benar. HTTP Status Code dapat diakses melalui xhr.status:
1

var fungsiRespon = function () {

if (xhr.readystate === 4) {

if (xhr.status === 200) {

// proses data

} else if (xhr.status === 404) {

alert("Lokasi URL tidak ditemukan pada server!");

} else if (xhr.status === 500) {

alert("Server error!");

10
11

}
}

Sampai titik ini kita sudah memastikan server mengembalikan data yang
lengkap, tanpa pesan kesalahan. Selanjutnya kita dapat langsung mengakses
data yang diberikan oleh server, untuk diolah lebih lanjut. Data yang
diberikan oleh server dapat diakses melalui dua buah properti khusus:

xhr.responseText - mengambil respon server dalam format String

xhr.responseXML - mengambil respon server dalam objek XMLDocument

Setelah mendapatkan data dalam format sesuai kebutuhan kita, kemudian kita
dapat mengolah data tersebut sesuai keinginan. Jika data kembalian yang
diberikan dibuat dalam format JSON, kita harus terlebih dahulu mengakses data
melalui xhr.responseText. Pembahasan lebih lanjut mengenai hal ini akan
dilakukan pada bagian berikutnya.
Note
Cara pengambilan respon yang dibahas pada bagian ini adalah cara pengambilan
respon untuk perintah asinkronus. Ketika menggunakan perintah sinkronus, kita
cukup memasukkan kode pengolahan data setelah xhr.send karena kode akan
berhenti dieksekusi sampai xhr.send selesai dijalankan.

Pengolahan Data JSON


Seperti yang telah dijelaskan pada awal tulisan, meskipun AJAX memiliki kata
XML di dalamnya, pada prakteknya kebanyakan pertukaran informasi dan data
pada aplikasi web modern dilakukan melalui JSON. JSON merupakan
formatnative dari Javascript, di mana format penulisan JSON sama persis
dengan format penulisan objek pada Javacsript (yang bahkan nampak dari
namanya: JavaScript Object Notation).
Untuk bekerja dengan JSON dalam Javascript, kita dapat menggunakan
objek JSON yang hanya memiliki
dua method:JSON.parse dan JSON.stringify. JSON.parse mengambil sebuah string
JSON dan menjadikannya objek Javascript, sementara JSON.stringify melakukan
hal sebaliknya: mengubah objek Javascript menjadi string JSON yang valid.
Untuk dapat lebih mudah memahami kegunaan dari kedua method ini, kita akan
langsung melihat contoh penggunaannya.
Membaca JSON Menjadi Objek
JSON.parse menerima dua buah parameter, dengan parameter pertama berupa
string yang akan diubah menjadi objek dan parameter kedua yang bersifat
opsional:
1

var response = "{\"health\": 3000, \"mana\": 400, \"str\": 86, \"agi\": 63, \"int\":

var ResObj

= JSON.parse(response);

3
4

ResObj["health"]; // 3000

ResObj.mana;

// 400

Parameter kedua dari JSON.parse dapat kita gunakan untuk melakukan sesuatu
terhadap data pada string, sebelum data tersebut dimasukkan ke dalam objek.
Parameter kedua ini menerima sebuah fungsi dengan dua parameter yang masingmasing adalah nama atribut dan nilai atribut dari string JSON yang sedang
dibaca. Fungsi akan dijalankan terhadap semua atribut JSON yang ada, sehingga
kita dapat mengubah nilai dari atribut jika diinginkan. Nama umum dari
parameter kedua ini adalah reviver. Perhatikan kode berikut:
1
2

var reviver = function (k, v) {

3
4
5
6
7
8
9
10
11
12
13
14

console.log(k + ": " + v);


};

var data = '{"satu": 1, "dua": 2, "tiga": {"empat": 4, "lima": 5}}';

JSON.parse(data, reviver);
// Keluaran:
// satu: 1
// dua: 2
// empat: 4
// lima: 5
// tiga: [object Object]
// : [object Object]

Beberapa hal yang dapat kita pelajari dari kode di atas:

Fungsi revivier dijalankan ke masing-masing atribut JSON secara


berurutan (satu, dua, tiga).

Ketika menemukan atribut yang memiliki nilai objek (JSON)


lain, reviver terlebih dahulu diterapkan pada atribut objek yang di
dalam (pada contoh kita: empat dan lima tercetak terlebih dahulu
sebelum tiga).

Terdapat satu atribut tambahan yang tidak ada dalam data, yaitu cetakan
terakhir (: [object: Object]). Atribut ini mewakili keseluruhan JSON
yang kita kirimkan, dan akan selalu dibaca paling terkhir. Hal ini
dibuat agar kita juga dapat mengubah objek utama (misal:
menambahkan method) jika diperlukan. Kita tetap harus ingat untuk
menangani kasus ini bahkan ketika kita tidak mengubah objek utama,
biasanya dengan mengembalikan nilai awal (v pada kode di atas)
agar JSON.parse tidak mengembalikan undefined.

Untuk memperjelas masalah penanganan objek utama pada poin ketiga, perhatikan
kode berikut:

var kosong = JSON.parse(data, function (k, v) {

2
3

return v * 2;
});

4
5

var isi = JSON.parse(data, function (k, v) {

// if (k === "") return v;

if (k === "" || typeof v === "object") return v;

8
9
10

return v * 2;
});

11
12

kosong; // NaN

13

isi;

// {"satu": 2, "dua": 4, "tiga": {"empat": 8, "lima": 10}}

Pada kode di atas, kita dapat melihat bagaimana jika kita tidak melakukan
pengecekan nilai k kosong maka objek hasil tidak akan dikembalikan. Sebagai
latihan, coba ganti baris if pada fungsi milik isi dengan yang dikomentari
dan lihat hasilnya. Kegunaan dari cek typeof v === "object diberikan sebagai
latihan kepada pembaca.
Memanfaatkan pengetahuan ini, kita dapat mengembangkan fungsi reviver yang
membuat objek penuh beserta denganmethod baru seperti berikut:
1
2
3
4

var response = "{\"health\": 3000, \"mana\": 400, \"str\": 86, \"agi\": 63, \"int\":
var reviver = function (k, v) {
if (k === "") {
v.getStr = function () {

return v.str;

7
8
9
10
11

return v;
}

12
13
14
15
16
17
18

return v * 2;
};

var TransformedObj = JSON.parse(response, reviver);

TransformedObj["health"]; // 6000
TransformedObj.mana;

// 800

TransformedObj.getStr();

// 172

Selanjutnya, mari kita lihat bagaimana cara mengubah objek menjadi string
JSON.
Mengubah Objek Menjadi String
Selain menerima string JSON dan mengubahnya ke dalam objek Javascript,
seringkali kita juga perlu mengirimkan data baru ke server, yang berasal dari
objek Javascript. Untuk kasus ini kita memerlukan mekanisme untuk mengubah
objek menjadi string JSON. Javascript menyediakan method JSON.stringify untuk
melakukan hal ini. Penggunaan sederhanamethod ini sangat mudah:
1

var obj = {

model: "L925",

size: 3.5,

age: 18,

conn: "4G"

};

7
8

var jsonString = JSON.stringify(obj);

9
10

jsonString; // '{"model":"L925","size":3.5,"age":18,"conn":"4G"}'

Begitupun, JSON.stringify memiliki parameter kedua dan ketiga yang sifatnya


opsional namun cukup berguna. Parameter kedua dari JSON.stringify mirip
dengan parameter kedua dari JSON.parse, yaitu sebuah fungsi yang digunakan

untuk melakukan proses data dalam objek sebelum objek tersebut dijadikan
string. Berikut adalah contoh penggunaannya:
1

var filteredJSON = JSON.stringify(obj, function (k, v) {

if (typeof v === "string") return undefined;

return v;

});

5
6

filteredJSON; // '{"size":3.5,"age":18}'

Seperti yang dilihat di atas, fungsi yang dikirimkan diterapkan ke semua


atribut objek untuk kemudian diproses. Kita juga dapat mengisikan array pada
parameter kedua. Jika diisikan dengan array, isi dari array akan dianggap
menjadi atribut yang ingin kita masukkan dalam string JSON, seperti berikut:
1

var altJSON = JSON.stringify(obj, ["model", "conn"]);

2
3

altJSON; // '{"model":"L925","conn":"4G"}'

Parameter ketiga dari JSON.stringify digunakan untuk memperindah string JSON


hasil proses. Kita dapat mengirimkan string yang dijadikan sebagai spasi
pemisah pada string JSON yang dihasilkan. Jika parameter ketiga diisikan
angka, maka angka tersebut akan menjadi jumlah spasi yang dimasukkan pada
pemisah string JSON. Langsung saja, berikut contoh penggunaannya:
1
2
3
4
5
6
7
8
9

JSON.stringfiy(obj, ["model", "conn"], "\t");


// keluaran:
// "{
//

"model": "L925",

//

"conn": "4G"

// }"

JSON.stringfiy(obj, ["model", "conn"], "aaaa");


// keluaran:

10

// "{

11

// aaaa"model": "L925",

12

// aaaa"conn": "4G"

13

// }"

14
15

JSON.stringfiy(obj, ["model", "conn"], 4);

16

// keluaran:

17

// "{

18

//

"model": "L925",

19

//

"conn": "4G"

20

// }"

WebSocket
WebSocket merupakan sebuah protokol komunikasi dua arah yang dapat digunakan
oleh browser. Jika pada AJAX kita hanya dapat melakukan komunikasi satu arah
dengan mengirimkan request kepada server dan menunggu balasannya, maka
menggunakan WebSocket kita tidak hanya dapat mengirimkan request kepada
server, tetapi juga menerima data dari server tanpa harus
mengirimkan request terlebih dahulu. Hal ini berarti ketika menggunakan
WebSocket pengguna harus terus menerus terkoneksi dengan server, dan kita
memerlukan sebuah server khusus untuk dapat menjalankan aplikasi WebSocket
dengan benar. Sederhananya, perhatikan gambar berikut:

Dari gambar di atas, kita dapat melihat bagaimana dalam kasus AJAX, setiap
request dari pengguna maupun respon dari server dilakukan secara terpisah.
Jika ingin mengirimkan request ke server, kita perlu membuka koneksi baru
yang kemudian akan dibalas oleh server seperti ia membalas permintaan halaman
HTML biasa. Server juga tidak dapat langsung melakukan pengiriman data ke
klien tanpa ada permintaan terlebih dahulu. Hal ini berbeda dengan pola
komunikasi WebSocket, di mana koneksi berjalan tanpa terputus dan server
dapat mengirimkan data atau perintah tanpa harus ada permintaan dari pengguna
terlebih dahulu. Dengan begitu, WebSocket bahkan juga memungkinkan server
mengirimkan data atau perintah ke semua klien yang terkoneksi, misalkan untuk
memberikan notifikasi global.
Untuk dapat melihat langsung bagaimana cara kerja WebSocket, kita akan
langsung melakukan eksperimen. Objek yang diperlukan untuk menggunakan
WebSocket dari browser adalah WebSocket. Tetapi sebelumnya, kita memerlukan
sedikit persiapan terlebih dahulu, yaitu komponen server dari WebSocket.
Komponen Server WebSocket

Teknologi WebSocket membutuhkan cara penggunaan yang sama sekali berbeda


dibandingkan dengan AJAX. Jika pada AJAX kita dapat mengguankan komponen
server yang sama dengan server HTTP pada umumnya, untuk WebSocket kita
memerlukan komponen server khusus. Hal ini disebabkan oleh model komunikasi
WebSocket yang mengharuskan pengguna terkoneksi dengan server sepanjang
aplikasi masih digunakan. Arsitektur server yang diperlukan untuk melayani
banyak koneksi terbuka seperti ini tentu berbeda dengan arsitektur yang
dibutuhkan untuk koneksi satu arah seperti pada HTTP. Ketika menggunakan
WebSocket, biasanya kita akan memerlukan komponen server (beserta kode sisi
server) khusus.
Adapun beberapa contoh komponen server untuk WebSocket yaitu:
1. Socket.IO (Javascript)
2. SignalR (.Net)
3. Jetty (JVM)
4. Event Machine (Ruby)
5. Tornado (Python)
Bahasa yang disebutkan pada daftar di atas adalah bahasa yang digunakan pada
sisi server.
Persiapan Eksperimen WebSocket
Sebelum kita dapat mencoba menggunakan WebSocket, terlebih dahulu kita akan
perlu membangun komponen server yang dapat mengerti protokol WebSocket, dan
dapat mengerti pesan yang akan kita kirimkan kepada mereka. Untuk memudahkan
pembaca, sebuah komponen server khusus telah disiapkan. Silahkan
download sampel komponen server (websocket-sample) ini terlebih dahulu.
Terdapat dua buah server yang diberikan, yaitu:
1. index.js merupakan komponen yang dirancang sebagai server dari sistem
chatting sederhana.
2. zero.js merupakan komponen yang dirancang sebagai server sistem
menonton game (seperti twitch.tv) sederhana.

Kita akan membangun klien dari index.js dari awal, sementara klien
untuk zero.js akan dimodifikasi dari contoh permainan sederhana yang sudah
dipersiapkan sebelumnya. Silahkan download juga kode untuk permainan
serderhana tersebut, dan coba pelajari cara kerjanya.
Menjalankan server WebSocket dari kedua kode tersebut juga sangat mudah,
cukup eksekusi kode dengan NodeJS:
1

$ node index.js

$ node zero.js

Setelah memilki semua komponen dasar yang diperlukan, kita akan mulai
mempelajari WebSocket dengan menelaah objek WebSocket terlebih dahulu.
Membuat Objek WebSocket
Pembuatan objek WebSocket (selanjutnya dirujuk sebagai ws saja) sangat mudah.
Kita dapat langsung memanggilconstructor dari objek tanpa perlu melakukan
pengecekan tambahan. Berikut adalah contoh pembuatan objek WebSocket baru:
1

var ws = new WebSocket("ws://www.example.com/socketserver");

Dan kita telah membuat sebuah objek WebSocket, sekaligus mencoba melakukan
koneksi ke alamat yang diberikan padaconstructor. Constructor dari ws juga
memiliki parameter kedua yang bersifat opsional. Parameter kedua ini dapat
berupa sebuah string ataupun array dari string, yang berisi nama dari
subprotokol WebSocket yang akan kita gunakan. Subprotokol merupakan pesan
tambahan yang dapat kita kirimkan ke server agar server mendapatkan informasi
tambahan yang dibutuhkan untuk memberikan respon. Subprotokol ini dibuat oleh
pengembang sendiri, dan baik klien maupun server harus dapat mengerti
subprotokol yang dikirimkan. Kegunaan utama dari subprotokol adalah agar baik
klien maupun server dapat yakin bahwa lawan bicaranya benar-benar mengerti
pesan yang dikirimkan maupun diterima. Kita tidak ingin server Dota2 versi 1
berkomunikasi dengan klien Dota2 versi 2 misalnya.
Penambahan subprotokol pada constructor WebSocket tidak rumit sama sekali:
1

var ws1 = new WebSocket("ws://www.example.com/socketserver", "dota2v1");

var ws2 = new WebSocket("ws://www.example.com/socketserver", ["dota2v1", "dota2v2"]);

Ketika kita mengirimkan array sebagai parameter kedua, berarti kita


memberitahukan ke server bahwa klien yang dibangun dapat mengerti semua
protokol yang ada di dalam array tersebut. Begitupun, pada akhirnya server
lah yang menentukan protokol mana yang akan digunakan. Klien kemudian dapat
mengelola data atau perintah sesuai dengan protokol yang digunakan server.
Menanggapi Server WebSocket
Setelah membuka koneksi, tentunya kita ingin berinteraksi dengan server.
Pertama-tama, mari kita lihat terlebih dahulu bagaimana cara menangani
perintah maupun data yang diberikan oleh server kepada kita.
Terdapat empat method utama yang dapat kita gunakan untuk menangani data dan
perintah yang dikirimkan server:
1. ws.onerror, method yang dijalankan ketika terjadi kesalahan pada
koneksi dengan server. Bersifat opsional.
2. ws.onclose, method yang dijalankan ketika koneksi dengan server telah
ditutup. Bersifat opsional. Memiliki satu parameter, CloseEvent yang
menyimpan detil pesan kesalahan yang terjadi.
3. ws.onopen, method yang dijalankan pertama kali ketika telah tersambung
dengan server. Bersifat opsional.
4. ws.onmessage, method yang dijalankan ketika menerima pesan dari server.
Memiliki satu parameter yang berisiMessageEvent, sebuah
objek event yang juga menyimpan data berupa pesan dari server melalui
propertiMessageEvent.data.
Keempat method yang ada di atas dapat digunakan semuanya ataupun
hanya ws.onmessage saja. Karena model pemrograman yang bersifat *event
based*, kita cukup hanya menspesifikasikan fungsi yang dijalankan pada
setiap eventdi atas saja. Browser akan secara otomatis mengeksekusi kode
ketika event terjadi. Berikut adalah contoh penggunaan keempat fungsi di
atas:

var ws = new WebSocket("ws://www.example.com/socketserver");

2
3

ws.onopen = function() {

4
5

console.log("Koneksi berhasil!");
};

6
7

ws.onerror = function () {

8
9

console.log("Koneksi gagal.");
};

10
11

ws.onclose = function (event) {

12
13

console.log("Koneksi telah ditutup. Pesan penutupan: " + event.code);


};

14
15

ws.onmessage = function (message) {

16

var data = JSON.parse(message.data);

17
18
19

// lakukan sesuatu dengan data


};

Perhatikan bagaimana kita menggunakan JSON.parse untuk melakukan pembacaan


data pada ws.onmessage. Meskipun kita dapat menggunakan format lain seperti
XML atau bahkan Blob untuk data yang diberikan server, JSON merupakan format
yang paling umum. Intinya adalah kita dapat menggunakan format apapun untuk
bertukar data, selama klien dan server mengerti format tersebut.
Berbicara dengan Server
Mengirimkan data atau perintah ke server WebSocket dapat dilakukan dengan
menggunakan method ws.send. Penggunaannya sangat gamblang:
1
2
3
4

ws.send("Data Server"); // mengirimkan string

// atau bahkan

var obj = {

message: "Sebuah Pesan Rahasia",

key: "F5-A1-00-32-E4",

command: "Run"

};

9
10

ws.send(JSON.stringify(obj));

ws.send menerima hanya satu parameter, yang dapat


berisi DOMString, ArrayBuffer, maupun Blob. Pada kode di atas kita hanya
menggunakan string (DOMString) saja, bahkan untuk objek yang akan dikirimkan
ke server.
Studi kasus
Sekilas penggunaan WebSocket terlihat cukup mudah dan sederhana. Untuk lebih
mendalami pengertian akan WebSocket, kita akan langsung mencoba membangun
aplikasi klien WebSocket. Ingat, terlebih dahulu kita perlu server khusus
dalam ujicoba ini. Silahkan lihat bagian Persiapan Eksperimen WebSocket untuk
detail aplikasi server yang dibutuhkan dalam eksperimen ini.
Kasus 1: Aplikasi Chatting
Aplikasi yang paling pertama akan kita kembangkan adalah aplikasi chatting
sederhana. Agar dapat berfokus pada pengiriman dan penerimaan pesan saja,
kita akan menggunakan kode HTML yang sangat sederhana, sebagai berikut:
1
2
3
4

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

5
6
7
8
9

<title>Websocket test</title>
</head>
<body>
<ul id="chat"></ul>

<input type="text" id="chatinput">

10
11
12
13

<script type="text/javascript" src="chat.js"></script>


</body>
</html>

Selanjutnya, kita akan mengisikan chat.js dengan kode koneksi ke server


WebSocket, sesuai dengan yang kita pelajari sejauh ini:
1
2

var chat

= document.querySelector("#chat"),
chatinput = document.querySelector("#chatinput"),

websocket = new WebSocket("ws://127.0.0.1:8080"), // #1

addChat

newText = document.createTextNode(data);

7
8

newChat.appendChild(newText);

chat.appendChild(newChat);

10

};

11

13
14
15
16
17
18
19
20
21
22
23
24

// #2

var newChat = document.createElement("li"),

12

= function (data) {

// #3
websocket.onmessage = function (event) {
addChat(event.data);
}

// #4
chatinput.addEventListener("keydown", function (evt) {
if (evt.keyCode === 13) {
addChat(chatinput.value);
websocket.send(chatinput.value); // #5
chatinput.value = "";

return false;

25
26

}
});

Penjelasan kode (sesuai dengan nomor yang diberikan pada komentar):


1. Pembuatan objek WebSocket untuk membuka koneksi dengan server. Pastikan
IP dan port yang digunakan benar pada bagian ini.
2. Fungsi addChat merupakan fungsi yang akan kita gunakan untuk
menambahkan chat baru ke elemen #chatinput.
3. Setelah membuat semua variabel dan fungsi yang dibutuhkan, kita
kemudian mengikatkan WebSocket.onmessagekepada satu fungsi anonim.
Fungsi ini tugasnya sangat sederhana: menambahkan chat baru dengan data
yang didapatkan dari server. Hal ini berarti chat baru yang ditambahkan
melalui WebSocket.onmessage selalu adalah chat yang dikirimkan oleh
pengguna lain.
4. Pada langkah ini kita menambahakn pemantau event keydown pada #chat,
yang akan mengirimkan data ke server ketika pengguna menekan tombol
Enter (key code 13).
5. Mengirimkan data ke server sangat sederhana, cukup dengan satu baris
perintah ini saja.
Seperti yang dapat kita lihat, implementasi WebSocket, baik dari sisi
pengiriman data ke server maupun pengolahan data yang diterima dari server
cukup mudah. Yang menjadi tantangan dalam penggunaan WebSocket biasanya
adalah kapanpengiriman data dilakukan, dan apa yang harus dilakukan terhadap
data yang dikirimkan oleh server.
Berikut adalah tampilan akhir dari aplikasi yang kita kembangkan:

Pemrograman Asinkron

Pada bagian sebelumnya, kita telah melihat dua model pemrograman asinkron,
yaitu AJAX dan WebSocket. Kedua model pemrograman ini memiliki pola
pemrograman yang sama, yaitu:
1. Kirimkan data atau perintah ke server secara asinkron.
2. Buat kode yang akan menangani respon dari server.
3. Lanjutkan kode aplikasi.
4. Ketika server telah mengembalikan data, jalankan kode pada no 2.
Model pemrograman yang menunggu sesuatu terjadi untuk kemudian menjalankan
kode tertentu seperti ini kita kenal dengan nama Event-Based
Programming (EBP). Meskipun dapat bekerja dengan sangat baik, model EBP
seperti ini cukup kompleks dan dapat menyebabkan kode program menjadi sulit
untuk dirawat. Misalkan kode seperti berikut:
1

$.ajax("/feature/", function(html) {

// (#1) melakukan sesuatu

3
4

$.ajax("/feature/subfeature", function(css) {

// (#2) melakukan sesuatu yang lebih kompleks

6
7

$.ajax("/feature/subfeature2", function() {

// (#3) melakukan sesuatu lagi

});

10
11

// (#4) lanjut eksekusi #2

12

});

13
14
15

// (#5) lanjut eksekusi #1


});

Pada format pemrograman di atas yang mengandalkan fungsi anonim


sebagai callback terhadap sebuah event yang terjadi menyebabkan perawatan
program lebih kompleks dari seharusnya. Jika misalnya terdapat kesalahan pada
bagian #3, kita akan kesulitan mendapatkan informasi lengkap (biasanya *stack

trace*) dari kode di atas. Kesulitan ini disebabkan berlapisnya fungsi anonim
yang digunakan, dan terkadang pesan kesalahan atau Exception yang dilemparkan
oleh fungsi anonim di dalam belum tentu dapat dibaca oleh fungsi luarnya.
Selain masalah di atas, umumnya juga sering terjadi kesulitan dalam mengikuti
alur eksekusi kode program seperti di atas. Contohnya, ketika #2 berjalan,
fungsi yang menampung #3 dapat saja dijalankan sebelum atau sesudah #4,
tergantung dari banyak faktor. Hal yang sama juga ditemui untuk
eksekusi #2 dan #5 (otomatis diikuti oleh eksekusi #3 serta #4). Masalah lain
lagi yaitu ketika kode di bagian #4 bergantung pada hasil yang ada pada #3.
Kita harus menggunakan mekanisme event yang lebih kompleks lagi,
yaitu callback, untuk memastikan #4 berjalan setelah #3.
Permsalahan-permasalahan yang dikemukakan di atas kemudian melahirkan
konstruk pemrograman baru

[1]

, yang diharapkan dapat membantu kita dalam

menulis program asinkron.


Promise
Promise merupakan sebuah objek yang merepresentasikan sebuah nilai yang belum
ada sekarang, tetapi akan ada pada satu titik di masa depan. Contohnya, kita
dapat menggunakan Promise untuk mengambil data dari lokasi luar (misal:
datatweet pada Twitter). Ketika Promise dibuat, nilai dari data yang belum
datang tidak mungkin diketahui. Untuk menanggulangi hal ini, Promise akan
bertindak sebagai penengah sementara sampai data selesai diambil. Sementara
Promise sedang bekerja di balik layar, kita dapat memperlakukan objek Promise
layaknya nilai kelas pertama. Hal ini berarti kita dapat menyimpannya ke
dalam variabel, mengirimkan objek ke fungsi, mengembalikan objek dari fungsi,
dan apapun yang dapat dilakukan oleh variabel lainnya.
Kemampuan untuk memperlakukan operasi asinkron layaknya sebuah nilai kelas
pertama ini akan sangat memudahkan kita dalam melakukan pemrograman asinkron.
Sederhananya, kita dapat menulis kode asinkron yang seolah-olah adalah kode
sinkron. Hal ini tentunya akan sangat menyederhanakan kode program kita, yang
pada akhirnya berimbas pada kemudahan perawatan kode.
Pembuatan Objek Promise
Membuat objek Promise sendiri dapat dilakukan dengan sangat sederhana:
1
2
3

var promise = new Promise(function (resolve, reject) {


// Kode asinkron (AJAX, WebSocket, dll)

if (/* semuanya lancar tanpa error */) {

resolve(data);

} else {

reject(Error("Terjadi kesalahan."));

});

Kita hanya cukup memanggil constructor dari objek promise, yang menerima satu
fungsi. Fungsi yang diberikan kepadaPromise dapat diisikan dengan dua
parameter, yaitu:
1. Parameter pertama yang adalah sebuah fungsi yang dijalankan jika kode
asinkron telah selesai berjalan, dan berjalan tanpa masalah.
2. Parameter kedua sifatnya opsional, berisi fungsi yang dijalankan ketika
terjadi kesalahan dalam eksekusi kode asinkron.
Isi dari badan fungsi yang dikirimkan ke Promise sendiri tidak dibatasi.
Misalnya, kita dapat mengisikan fungsi tersebut dengan pemanggilan AJAX
seperti berikut:
1
2
3
4
5
6
7
8
9

var promise = new Promise(function (resolve, reject) {


var xhr;

try {
xhr = new XMLHttpRequest();
} catch (e) { reject(Error("XHR tidak didukung browser.")); }

xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {

10

resolve(xhr.responseText);

11
12
13
14

}
};

xhr.open('GET', 'http://example.com/testajax/');

15

xhr.send();

16
17

});

Seperti yang dilihat dari kode di atas, kita pada dasarnya melakukan
pemanggilan AJAX di dalam fungsi yang dikirimkan kePromise. Perbedaan dari
kode yang kita tulis di atas adalah pemanggilan
fungsi resolve dan reject ketika kode AJAX berhasil ataupun gagal berjalan.
Sebuah Promise yang telah kita buat dapat memiliki tiga status keadaan:
1. Pending, yaitu jika nilai hasil eksekusi asinkron Promise belum dapat
diakses.
2. Fulfilled, yaitu ketika nilai sudah dapat diakses. Nilai ini nantinya
akan menjadi nilai permanen dari Promise.
3. Rejected, yaitu nilai yang didapatkan kalau eksekusi asinkron gagal
berjalan. Sama seperti fulfilled, nilai dari rejectedakan diasosiasikan
kepada Promise secara permanen. Nilai rejected biasanya berupa
objek Error, meskipun tidak terdapat batasan khusus apa yang harus kita
isikan di sini.
Ketika Promise dibuat, secara otomatis Promise akan berada pada
status pending. Kita kemudian dapat menggunakanPromise selayaknya variabel
biasa. Utilisasi dari hasil eksekusi asinkron Promise (setelah status berubah
menjadi fulfilledatau rejected) sendiri harus dilakukan melalui
pemanggilan method khusus.
Mengambil Hasil Eksekusi Promise
Meskipun Promise dapat digunakan layaknya variabel sembari menjalankan
eksekusi asinkron di balik layar, sampai satu titik tertentu kita tetap akan
harus mendapatkan hasil eksekusi tersebut. Ketika ingin mengakses nilai hasil
eksekusi asinkron dari Promise kita dapat memanggil method then, seperti
berikut:
1
2
3

promise.then(
function (data) {

// jika promise sukses dijalankan

},

function (error) {

// jika promise gagal dijalankan

7
8

}
);

Method then menerima dua argumen: sebuah fungsi yang dipanggil


ketika Promise selesai dijalankan (fulfilled), dan sebuah fungsi yang
dijalankan ketika Promise gagal dijalankan (rejected). Kedua parameter ini
sifatnya opsional, sehingga kita dapat menambahkan hanya salah satu dari
fungsi tersebut.
Beberapa hal yang perlu diingat mengenai akses hasil eksekusi
dan method then:
1. Sebuah Promise hanya bisa berhasil atau gagal dieksekusi satu kali
saja. Sebuah Promise yang sudah berhasil atau gagal dijalankan juga
tidak dapat berubah status (misal: awalnya gagal, kemudian menjadi
berhasil). Hal ini berarti hasil eksekusi Promise akan selalu konsisten
begitu selesai dijalankan.
2. Jika Promise telah selesai dijalankan (tidak perduli berhasil atau
gagal), dan kita memasukkan fungsi baru untuk dijalankan jika
berhasil, Promise akan memanggil kembali fungsi tersebut dengan data
terakhir.
Kedua hal di atas sangat membantu kita dalam melakukan pemrograman asinkron,
karena kita tidak lagi harus pedulikapan sesuatu dapat diakses, melainkan apa
yang harus dilakukan ketika hal tersebut dapat diakses.
Promise Berantai
Seringkali ketika kita melakukan operasi asinkron, kita ingin melanjutkan
satu operasi asinkron dengan operasi asinkron lainnya. Misalnya, katakanlah
kita ingin mengambil data timeline dari Twitter beberapa pengguna, dan
kemudian menyusun data tersebut sesuai tanggal penulisan. Ketika pengambilan
data dari beberapa pengguna, tentunya kita harus melakukan beberapa
pemanggilan asinkron secara berkesinambungan dan memastikan seluruh
pemanggilan tersebut telah selesai sebelum mulai menyusunnya.

Kita dapat menggunakan dua cara untuk melakukan pemanggilan Promise secara
berantai seperti yang dijelaskan sebelumnya, yaitu:
1. Mengembalikan objek Promise pada fungsi yang dikirimkan ke then, dan
kemudian memanggil then dari fungsi tersebut lagi.
2. Menggunakan method Promise.all yang dirancang untuk memanggil
beberapa Promise sekaligus.
Mari kita langsung lihat metode yang pertama terlebih dahulu. Asumsikan kita
memiliki sebuah fungsi yang mengembalikan Promise seperti berikut:
1

// MyLib.AJAX merupakan fungsi untuk pemanggilan AJAX

// yang dibahas pada bab sebelumnya.

var get = function (url, success, fail) {

MyLib.AJAX({

onSuccess: success,

onFailed: fail,

url: url,

method: "GET"

});

10

},

11

getPromised = function (url) {

12

return new Promise(function (resolve, reject) {

13

get(url, resolve, reject);

14
15

});
};

Kita kemudian dapat menggunakan getPromised seperti berikut:


1
2
3
4
5
6
7

var promise = getPromised("https://example.com/api/testing");

promise.then(function (data) {
// lakukan sesuatu
});

// atau langsung:

getPromised("https://example.com/api/testing").then(function (data) {

9
10

// lakukan sesuatu
});

Ketika kita mengembalikan nilai dari fungsi yang diberikan pada then, kita
dapat menyambung pemanggilan then untuk melakukan operasi lebih lanjut kepada
nilai tersebut:
1

getPromised("https://example.com/api/testing").then(function (data) {

// lakukan sesuatu

3
4

console.log(data); // asumsi: data = 10

return data + 1;

}).then(function (val) {

7
8

console.log(val); // 11 (data + 1)
});

Dengan prinsip yang sama, ketika kita mengembalikan objek Promise di dalam
fungsi then, maka operasi Promise pada fungsi kedua hanya dapat
dijalankan setelah operasi pertama selesai. Perhatikan kode berikut:
1
2

getPromised("https://example.com/api/testing")
.then(function (data) {

// lakukan sesuatu

4
5

// ...

6
7

return getPromised("https://example.com/api/percobaan");

})

.then(function (data) {

10

// fungsi ini hanya berjalan SETELAH

11

// then pertama selesai.

12

});

Karena then yang pertama baru akan dijalankan setelah pemanggilan


ke https://example.com/api/testing selesai, maka dengan mengembalikan

sebuah Promise baru kita akan memastikan then kedua hanya berjalan
setelah Promiseselesai berjalan.
Cara lain untuk menjalankan beberapa Promise sekaligus adalah dengan
menggunakan method Promise.all. MethodPromise.all menerima sebuah array
dari Promise, yang akan dijalankan dan dikembalikan hasilnya kepada kita pada
fungsi then:
1

var p1

= getPromised("https://example.com/api/testing");

var p2

= getPromised("https://example.com/api/percobaan");

var allP = [p1, p2];

4
5

Promise.all(allP).then(function (results) {

// results adalah array berisi hasil dari

// p1 dan p2, berurutan

});

Seperti yang dapat dilihat pada kode di atas,


fungsi then pada Promise.all sedikit berbeda, karena memberikan kita akses
kepada hasil dari eksekusi seluruh promise yang dikirimkan, secara berurutan
di dalam sebuah array. Sedikit kekurangan dari Promise.all adalah jika
terdapat satu saja Promise yang gagal dijalankan, maka semua Promise yang
dikirimkan akan dianggap gagal.
Perlombaan Promise
Ketika mengembangkan aplikasi yang bergantung kepada data asinkron, terkadang
kita hanya memerlukan salah satu sumber data saja, tetapi menggunakan
beberapa sumber untuk mendapatkan data yang tercepat ataupun terbaik.
Misalnya, kita sedang mengembangkan aplikasi pemetaan yang mengambil data
dari Google Maps (GM) dan Open Street Map (OSM). Kita dapat saja ingin
menampilkan peta secepatnya, tidak peduli apakah data peta berasal dari GM
atau OSM. Setelah mendapatkan peta, tentunya kita tidak ingin mengambil peta
yang satunya lagi, untuk menghemat bandwidthpengguna. Untuk dapat melakukan
hal ini, kita dapat menggunakan method Promise.race.
Promise.race menjalankan beberapa Promise sekaligus, dan kemudian mengambil
hasil Promise pertama yang selesai (atau gagal) dieksekusi.
Objek Promise lainnya yang belum selesai dieksekusi akan dihentikan begitu
kita mendapatkan hasil. Cara kerjanya cukup sederhana, yaitu dengan

menjalankan semua Promise bersamaan dan mengambil yang pertama kali


memberikan respon. Berikut adalah contoh penggunaannya:
1

var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "one"); });

var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "two");

3
4
5
6

Promise.race([p1, p2]).then(function(value) {
// value === "two"
});

Sama seperti Promise.all, kita mengirimkan sebuah array berisi


beberapa Promise ke Promise.race. Fungsi thenkemudian akan memberikan satu
nilai saja, yaitu nilai yang terlebih dahulu didapatkan. Dalam contoh kode di
atas, kita dapat melihat bahwa p2 pasti akan selesai dijalankan terlebih
dahulu, sehingga nilai value dapat dipastikan adalah two. Begitu kita
mendapatkan hasil dari p2, nilai dari p1 sudah menjadi tidak penting
karena value akan diisikan oleh hasil dari p2.
Penggunaan nilai awal ini juga berlaku bahkan ketika Promise yang lebih akhir
selesai gagal dijalankan:
1

var p3 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "three

var p4 = new Promise(function(resolve, reject) { setTimeout(reject, 500, "four")

3
4

Promise.race([p3, p4]).then(

function(value) {

// value === "three"

},

function(reason) {

// tidak dieksekusi

10
11

}
);

Bahkan ketika salah Promise terlebih dahulu gagal, Promise lain yang mungkin
memberikan hasil juga tidak lagi diperhitungkan hasilnya:
1
2

var p5 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "five"

3
4
5

var p6 = new Promise(function(resolve, reject) { setTimeout(reject, 100, "six");

Promise.race([p5, p6]).then(
function(value) {

// tidak dieksekusi

}, function(reason) {

// reason === "six"

9
10

}
);

Berdasarkan prilaku dari Promise.race yang mengedepankan Promise tercepat


inilah kita memberikan nama Perlombaan Promise.

Javascript History API


Location Bar (tempat mengisikan URL) dan tombol Back merupakan dua elemen
yang paling banyak digunakan pada sebuah browser

[1]

. Sebagai elemen

terpopuler, tentunya sangat banyak pengguna yang bergantung pada kedua elemen
ini dalam melakukan navigasi website. Ketika ingin mengunjungi sebuah web,
kita memasukkan alamat web tersebut ke dalam Location Bar. Jika kita
berpindah-pindah halaman dalam satu jendela browser, kita lalu dapat kembali
ke halaman sebelumnya melalui tombol Back. Model navigasi seperti ini
merupakan model yang paling umum digunakan oleh pengguna awam.
Di dalam sebuah website sendiri biasanya terdapat elemen-elemen yang tidak
berubah antar halaman web, misalnya menu atau logo dari website. Ketika
pengguna berpindah dari satu halaman ke halaman lain maka pengguna harus
mengambil kembali elemen-elemen yang sama tersebut. Pengembang web modern
menghindari hal ini dengan menggunakan teknik seperti AJAX yang dapat
memperbaharui halaman web dengan menambah atau mengubah bagian-bagian yang
perlu diganti tanpa pengguna harus berpindah halaman. Penggunaan AJAX untuk
navigasi web seperti ini, sayangnya membuat tombol Back tidak lagi dapat
digunakan. AJAX memperbaharui halaman web tanpa berpindah halaman ini
maka browser tidak dapat mencatat sejarah browsing, yang menyebabkan tombol
Back tidak dapat digunakan. Hal ini seringkali mengecewakan para pengguna.
History API dari Javascript kemudian dikembangkan untuk menyelesaikan
permasalahan tombol Back yang tidak dapat digunakan pada website dengan fitur
AJAX ini. Fitur utama yang ditawarkan oleh History API adalah penelusuran dan
manipulasi sejarah browser. Adapun mayoritas dari History API dapat diakses
melalui objek window.history.
Langsung saja, mari kita lihat beberapa fitur yang ditawarkan.
Penelusuran Sejarah Browser
Penelusuran sejarah browser dapat dilakukan dengan sangat mudah,
melalui method khusus yang sudah disediakan
yaituhistory.back dan history.forward:
1

// mundur satu halaman ke belakang

window.history.back();

history.back();

// fungsinya sama dengan di atas

4
5

// maju satu halaman ke depan

history.forward();

Kita juga dapat bergerak ke posisi spesifik dalam sejarah, dengan


menggunakan method history.go. Method history.gomenerima satu parameter,
yaitu sebuah angka yang menentukan sejauh mana kita ingin bergerak relatif
terhadap posisi sekarang dalam sejarah. Hal ini berarti nilai positif akan
membuat kita bergerak maju, dan nilai negatif membuat kita bergerak mundur
dalam sejarah browser.
1

history.go(-1); // bergerak mundur ke 1 halaman sebelumnya

history.go(-5); // bergerak mundur ke 5 halaman sebelumnya

3
4

history.go(1);

// bergerak maju 1 halaman

history.go(5);

// bergerak maju 5 halaman

Jika kita tidak mengetahui dengan pasti ada berapa halaman ke belakang maupun
ke depan, kita dapat menggunakan properti history.length untuk melihat ada
berapa banyak total sejarah yang sudah tercatat.
1

var jlhSejarah = history.length;

Dengan menggunakan fungsi maupun properti di atas kita dapat mempermudah


navigasi halaman pengguna melalui kode program. Selanjutnya, kita akan
melihat bagaimana secara langsung menambahkan data sejarah baru ke browser.
Manipulasi Sejarah Browser
Terdapat dua method utama yang akan kita gunakan untuk melakukan manipulasi
sejarah browser,
yaituhistory.pushState dan history.replaceState. history.pushState berguna
untuk menambahkan catatan sejarah baru ke dalam browser,
sementara history.replaceState digunakan untuk memodifikasi catatan sejarah
terakhir (halaman yang sedang dibuka pengguna).
Langsung saja, kita akan melihat cara kerja dari masing-masing fungsi yang
disediakan untuk manipulasi sejarah browser.

Fungsi history.pushState
Seperti yang dijelaskan sebelumnya, history.pushState menambahkan sejarah
baru ke dalam browser. Hal ini berarti setiap kali kita
memanggil history.pushState browser akan memindahkan halaman yang sekarang
dengan data baru ke dalam history stack. Hal ini sedikit berbeda dengan
navigasi biasa yang mana browser memasukkan URL halaman baru ke dalam history
stack dan kemudian melakukan download kepada halaman baru tersebut.
history.pushState menerima tiga buah parameter, yaitu:
1. Sebuah objek yang menyimpan data sejarah. Objek ini dapat diisikan
dengan data apapun yang kita inginkan, dan idealnya berisi data yang
digunakan untuk menandai status halaman yang dibuka. Objek ini disebut
dengan objekstate. Objek state disimpan di dalam komputer lokal
pengguna, dengan batas maksimal 640k karakter ketika objek diserialkan
(misal dengan memanggil JSON.stringify). Jika memerlukan objek yang
lebih besar dari itu, gunakansessionStorage dan localStorage.
Objek state ini nantinya akan diberikan ke halaman yang sedang aktif
ketika pengguna menekan tombol Back (atau kembali ke halaman sebelumya
denganhistory.back maupun history.go). Objek diberikan
melalui event popstate.
2. Parameter kedua merupakan sebuah string, yang memberikan nama dari
sejarah yang akan dikunjungi. Sayangnya, parameter kedua ini belum
didukung mayoritas browser pada masa penulisan (September 2014)
sehingga parameter ini tidak melakukan apa-apa.
3. Sebuah string yang berisi URL baru. Browser tidak akan pindah ke URL
baru ini ketika kita memanggil pushState, melainkan hanya menggantikan
URL yang ada dengan URL baru ini. Begitupun, terdapat kasus di
mana browser akan membuka URL ini, misalnya ketika kita mematikan dan
menjalankan kembali browser. Parameter ketiga ini dapat diisikan dengan
URL relatif, tetapi harus dari domain yang sama. Jika URL yang
diberikan memiliki domain berbeda maka sebuah exception akan
dilemparkan. Parameter ketiga bersifat opsional. Jika tidak diberikan,
parameter ketiga diasumsikan adalah alamat URL sekarang.
Ketika menggunakan history.pushState, hal yang paling perlu diperhatikan
adalah data yang kita berikan pada parameter pertama. Sebagai data yang
menandakan keadaan (state) dari sebuah sejarah yang baru dibuat, biasanya

data ini lah yang menentukan tampilan atau bentuk halaman yang akan kita
berikan ke pengguna. Misalnya pada sebuah daftar artikel yang disimpan dalam
beberapa halaman, kita dapat mengirimkan nomor halaman selanjutnya seperti
berikut:
1

var data = {

2
3

hal: halSelanjutnya
};

4
5

history.pushState(data, "Halaman " + halSelanjutnya, "/artikel/halaman/" + halSelanju

Perhatikan bahwa data yang kita kirimkan berisi halaman selanjutnya, yakni
halaman yang akan kita tampilkan bukan halaman tempat sekarang pengguna
berada. Data yang kita kirimkan ini akan dapat diakses ketika pengguna
menekan tombol Back dari halaman berikutnya. Data kita akses
melalui event popstate.
Fungsi history.replaceState
history.replaceState merupakan method yang cara kerjanya sama persis
dengan history.pushState. Perbedaan utama method ini
dengan history.pushState adalah history.replaceState tidak menambahkan
catatan sejarah baru, melainkan langsung menggantikan state halaman yang
sedang dibuka sekarang.
Method ini sangat berguna terutama ketika kita ingin menambahkan state baru
ke halaman, berdasarkan apa yang dilakukan oleh pengguna.
Event PopState
Event PopState dijalankan oleh browser setiap kali terjadi pergerakan isi
sejarah dari satu dokumen yang sama. PopState hanya berjalan ketika
pergerakan sejarah dilakukan secara langsung atas permintaan pengguna, dengan
kata lain penekanan tombol Back dan Forward
atau history.back maupun history.go akan menjalankan PopState. PopState tidak
akan dijalankan
ketika history.pushState maupun history.replaceState dijalankan.

Ingat juga bahwa PopState hanya berjalan untuk satu dokumen yang sama. Hal
ini berarti ketika pengguna berpindah dokumen (misal
dari index.html ke about.html), maka PopState tidak akan dijalankan ketika
pengguna menekan tombol Back. Ketika mengembangkan aplikasi dengan History
API, kita hanya akan memiliki satu dokumen web saja, untuk menjaga
keberjalnjutan sejarah ini. Karenanya, aplikasi yang dibangun dengan
memanfaatkan History API sebagai pengendali dokumen dikenal dengan
nama Single Page Application (SPA).
Penggunaan event PopState sendiri tidak berbeda dengan event lainnya:
1

window.addEventListener("popstate", function (event) {

var stateData = event.state;

3
4

// kembalikan halaman ke state sebelumnya,

// sesuai dari data pada stateData

});

Beberapa hal yang perlu diperhatikan dari contoh penggunaan PopState di atas:
1. PopState diikatkan pada objek window. Hal ini penting, karena PopState
yang sifatnya global dan untuk seluruh halaman ini hanya dimiliki
oleh window saja.
2. Data yang kita akses pada event.state adalah data yang kita berikan
untuk parameter pertama
padahistory.pushState atau history.replaceState. Isi
dari event.state ini akan terus berubah setiap kali pengguna menekan
tombol Back atau Forward.
Hal lain yang juga perlu diingat adalah bahwa PopState tidak dijalankan
ketika kita pertama kali membuka sebuah dokumen HTML. Begitupun, PopState
akan tetap dijalankan ketika kita kembali membuka halaman pertama melalui
tomblback.
Kegunaan Praktis History API
Seperti yang telah dijelaskan pada bagian awal, fungsi utama dari History API
adalah untuk memastikan sejarah dari penelusuran pengguna tersimpan dalam

browser, bahkan ketika aplikasi kita mengandalkan AJAX sebagai metode


navigasi utama. Dalam prakteknya, tentunya kita tidak dapat semata-mata
langsung menggunakan History API pada setiap pemanggilan kode AJAX. Jika
dilakukan perubahan URL dan sejarah secara manual pada setiap pemanggilan
AJAX, kode yang kita hasilkan akan menjadi sulit dibaca dan dirawat.
Yang umumnya dilakukan untuk menjaga kerapihan kode adalah mengembangkan
sebuah pustaka khusus untuk memetakan URL dengan kode kita. Pustaka yang
mengatur pemetaan antara URL dengan kode dikenal dengan namaRouting Library.
Kita akan mencoba mengimplementasikan routing library pada bagian berikutnya.
Catatan Kaki
[1]

Data dari Mozilla menunjukkan bahwa 95% pengguna menggu

Javascript Routing
Pada bagian ini kita akan membahas penggunaan History API untuk membangun
sebuah routing library sederhana. Tujuan utama dari pustaka yang ingin kita
kembangkan adalah untuk memetakan URL yang diakses user dengan fungsi atau
objek tertentu dalam kode kita. Misalkan ketika pengguna
mengakses http://contoh.com/profile/bertzzie maka
fungsiContoh.Profile("bertzzie") di dalam kode kita akan dipanggil. Karena
menggunakan History API, tentunya pengguna tidak perlu benar-benar berpindah
halaman (data bisa diambil menggunakan AJAX / WebSocket) dan sejarah
penelusuran dalam browser akan tetap diperbaharui.
Contoh penggunaan

Agar pembaca dapat lebih mudah memahami apa yang akan kita kembangkan, kita
terlebih dahulu akan melihat contoh penggunaan routing library yang akan
dikembangkan. Berikut adalah contoh penggunaan routing library yang akan kita
kembangkan:
1

var root

route

= "/routing/",

// route relatif dari http://contoh.com/routing/

= MyLib.Routes(root); // pembuatan objek routing

3
4

route.add(/profile\/(.*)/, function (pid) {

// kode dijalankan ketika pengguna mengakses

// http://contoh.com/routing/profile/*

});

8
9

route.add(/options\/(.*)/, function (type) {

10

// kode dijalankan ketika pengguna mengakses

11

// http://contoh.com/routing/options/*

12

});

13
14

route.init(); // inisialisasi semua route

Beberapa hal penting yang perlu diperhatikan dari kode di atas:


1. Pembuatan objek dilakukan melalui pemanggilan fungsi biasa,
yaitu MyLib.Routes.
2. Kita dapat memilih beberapa titik asal, yang disimpan pada
variabel root. Jika root berisi "/master/" maka seluruh route yang
dibuat akan dianggap relatif terhadap http://contoh.com/master/.
3. Penambahan route dilakukan dengan memanggil method Routes.add.
4. Pencocokan URL dilakukan menggunakan regular expression (dari parameter
pertama pada Routes.add).
5. Setelah semua route yang ada ditambahkan, kita harus tetap melakukan
inisialisasi melalui Routes.init.

Pada akhirnya, kode yang kita buat di atas cukup sederhana, dan terlihat
tidak jauh berbeda dengan kode frameworkaplikasi pada umumnya.
Dengan menggunakan routing library seperti pada kode di atas, secara otomatis
seluruh request yang datang dari pengguna akan hanya boleh datang dari satu
titik saja. Seluruh request harus masuk ke satu halaman yang sama, dan
kemudian request tersebut ditangani oleh kode router kita di atas. Karena
seluruh request masuk ke satu halaman (biasanyaindex.html), maka aplikasi
yang dibangun dengan cara seperti ini dikenal dengan nama Single Page
Application (SPA)

[1]

Persiapan Awal
Sebelum mulai mengembangkan routing library ini, terdapat satu hal yang perlu
kita persiapkan terlebih dahulu, yaitu memastikan semua request pengguna
masuk ke satu titik pada kode kita saja. Jika kita ingin menggunakan routing
librarydengan optimal, kita harus dapat memastikan request pengguna, dalam
bentuk apapun, kembali ke index.html. Hal ini berarti ketika pengguna
mengakses http://contoh.com/profile/bertzzie (atau URL lainnya) sebenarnya ia
akan dibawa ke http://contoh.com/index.html di balik layar.
Untuk dapat mencapai hal ini, kita perlu melakukan konfigurasi melalui web
server yang digunakan. Silahkan baca dokumentasi pada web server yang anda
gunakan untuk melakukan hal ini

[2]

. Berikut adalah contoh konfigurasi yang

dapat digunakan jika anda menggunakan web server Apache httpd (yang biasa
dipaketkan dalam XAMPP

[3]

):

1. Pastikan ekstensi mod_rewrite telah aktif pada server anda (tutorial


linux, tuorial windows).
2. Buat sebuah file bernama .htaccess pada direktori utama aplikasi web
anda.
3. Isikan .htaccess dengan kode berikut:
1

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^(.*)$ index.html

Kemudian coba akses halaman secara acak pada URL relatif terhadap aplikasi
web anda. Jika anda menyimpan kode dihtdocs/routing/ maka akses

ke http://localhost/routing/url/acak/asdflak akan tetap menampilkan


isiindex.html sekarang.
Objek Pendukung
Hal lain yang perlu kita persiapkan sebelum mulai membangun routing
library kita adalah objek pendukung yang akan membantu kita dalam memproses
string URL yang akan kita olah. Kita akan mulai dengan membuat sebuah objek
sederhana. Buat sebuah file dengan nama URL.js dan isikan dengan kode
berikut:
1

var MyLib = MyLib || {};

2
3
4

(function () {
"use strict";

5
6

MyLib.URL = {

trimSlash: function (path) {

return path.toString().replace(/\/$/, '').replace(/^\//, '');

},

10

getPathName: function (url) {

11

var a

= document.createElement("a");

12

a.href = url;

13
14

return a.pathname;

15

16

};

17

})(MyLib);

MyLib.URL merupakan sebuah objek sederhana yang hanya memiliki dua


buah method, yaitu:
1. trimSlash, sebuah method yang membantu kita dalam membuang garis miring
(slash; /) pada awal maupun akhir URL. Kita membuat method ini agar
rute seperti /profile/update/ dan /profile/update yang pada dasarnya
adalah sama, dapat dianggap sama oleh sistem kita nantinya. Selain
memotong garis miring pada akhir, kita juga memotong garis miring pada
awal, sehingga /route/ dan route dianggap sama.

2. getPathName, method yang mengambil pathname dari sebuah


URL. Pathname merupakan bagian URL yang muncul setelah URL dan
sebelum query data.
Contohnya, pathname dari http://www.google.com/search?q=testing adala
h/search. Kita ingin mengambil pathname karena biasanya bagian inilah
yang paling penting untuk routing (yang secara definisi menampilkan
halaman yang tepat untuk sebuah path).
Setelah membuat objek pendukung yang dibutuhkan, mari kita kembangkan objek
utama dari routing library kita.
Objek Router Utama
Langkah pertama untuk membuat routing library pastinya adalah membuat
objek router yang akan kita gunakan nantinya. Kode pembuatan objek sangat
sederhana, sesuai dengan prinsip awal yang kita pelajari pada Javascript:
Sebuah Pembedahan. Langsung saja, buat sebuah file dengan nama Routes.js,
dengan isi seperti berikut:
1

var MyLib = MyLib || {};

2
3
4

(function () {
"use strict";

5
6

MyLibs.Routes = function (r) {

var root

= r? MyLib.URL.trimSlash(r) : "/",

routes = [],

fin

= {};

10
11
12
13

return fin;
};
})();

Constructor objek utama kita menerima sebuah parameter opsional,


yaitu r. r merupakan akar atau rute awal yang kita inginkan untuk aplikasi
kita. Jika kita memasukkan nilai "/routing/" ke dalam r, maka semua rute yang
ditangani olehlibrary kita akan berawal
dari /routing/ (http://contoh.com/routing/profile ditangani
tetapihttp://contoh.com/about/profile tidak). Opsi ini tentunya sangat

berguna, memberikan fleksibilitas kepada kita untuk membangun apliksai dengan


beberapa titik awal rute berbeda. Jika dikosongkan, maka rute akar akan
otomatis menjadi/, yang artinya semua rute diproses.
Selanjutnya kita membuat sebuah array, routes, yang akan menyimpan seluruh
objek rute yang nantinya kita gunakan untuk memanggil fungsi yang dikaitkan
dengan rute. Rute dan fungsi yang akan dipanggil nantinya akan diisikan oleh
pengguna routing library kita. Setiap kali pengguna menambahkan rute baru
beserta dengan fungsi yang harus dipanggil ketika rute dipanggil, kita akan
membuat objek baru dan menyimpannya ke dalam array routes ini.
Terakhir, kita membuat objek fin, yang akan dikirimkan ke pengguna nanti.
Kita akan menambahkan banyak method ke dalam objek ini pada bagian
berikutnya.
Penambahan dan Penghapusan Rute
Fungsi paling pertama yang akan kita kembangkan ialah penambahan dan
penghapusan rute, karena fungsi inilah yang pertama kali akan digunakan oleh
pengguna. Langsung saja, tambahkan kode berikut sebelum return fin;:
1
2

fin.add = function (pattern, handler) {


routes.push({'pattern': pattern, 'handler': handler});

3
4
5
6
7
8

return this;
};

fin.remove = function (pattern) {


var i

len = routes.length,

r;

10
11
12

for (i = 0; i < len; i += 1) {


r = routes[i].pattern.toString();

13

if (r === pattern.toString()) {

14

routes.splice(i, 1);

15

return this;

16
17
18

= 0,

}
}

19
20

return this;
};

21
22

fin.reset = function () {

23

routes = [];

24

root

25

= ''

};

Penambahan rute baru, melalui fin.add cukup sederhana. Method ini menerima
dua argumen: argumen pertama beruparegular expression yang digunakan untuk
mencocokkan pola rute, sementara argumen kedua adalah fungsi yang dipanggil
ketika rute cocok dengan URL. Kita hanya menambahkan objek baru ke dalam
array routes, dan kemudian mengembalikan this agar method dapat dipanggil
secara berantai jika diperlukan.
Penghapusan rute pada fin.remove sedikit lebih panjang, karena kita terlebih
dahulu harus menelusuri seluruh rute yang ada. Setiap rute dicek
apakah pattern-nya sama dengan rute yang akan dihapus, dan jika sama rute
tersebut kita buang dari array routes. Pembuangan array dilakukan
melalui Array.prototype.splice (dokumentasi splice) untuk menyederhanakan
fungsi.
Sebagai tambahan, kita juga membuat fungsi fin.reset yang hanya menghapus
semua rute, beserta dengan rute asal.
Masuk ke Rute Baru
Setelah bisa menambahkan rute baru, tentunya kita ingin memberikan fasilitas
kepada pengguna library untuk berpindah rute. Misalkan, pengguna web dapat
dibawa ke rute baru ketika melakukan klik sebuah tombol. Berikut adalah kode
untuk mencapai perpindahan rute ini:
1
2
3
4
5
6
7

var Run = function (url) {


var path = url || fin.getCurrentPath(),
i

= 0,

len

= routes.length,

match;

path = MyLib.URL.trimSlash(path);

8
9

for (i = 0; i < len; i += 1) {

10

match = path.match(routes[i].pattern);

11

if (match) {

12

match.shift();

13

routes[i].handler.apply({}, match);

14

15
16
17
18
19

}
};

fin.getCurrentPath = function () {
var path = '';

20
21

path = MyLib.URL.trimSlash(location.pathname);

22

path = path.replace(/\?(.*)$/, '');

23

path = root != '/' ? path.replace(root, ''): path;

24
25
26
27
28
29

return MyLib.URL.trimSlash(path);
};

fin.navigate = function (path, title, state) {


path = MyLib.URL.trimSlash(MyLib.URL.getPathName(path));

30

Run(path);

31
32
33

history.pushState(state, title, "/" + root + "/" + path);


};

Run merupakan sebuah fungsi privat yang melakukan pencocokan pola URL
terhadap regular expression dari rute yang kita berikan pada fin.add. Jika
kita tidak memberikan nilai url kepada Run, maka eksekusi akan dilakukan
terhadap URL yang sedang dibuka pengguna pada saat itu (sehingga pengguna
tidak berpindah halaman). Pada fungsi Run, pada dasarnya kita menelusuri satu
per satu rute, dan mencocokkan polanya dengan

menggunakan methodString.prototype.match (dokumentasi match.


Karena String.prototype.match mengembalikan sebuah array sesuai dengan pola
yang kita berikan, kita kemudian dapat menggunakan fungsi apply

[4]

untuk

memanggil fungsi handleryang kita simpan ketika memanggil fin.add. Tentu saja
pemanggilan apply dilakukan setelah kita membuang elemen pertama dari array
hasil, karena elemen pertama merupakan pola dasar URL yang diberikan
(contohnya"/profile/bertzzie" dengan pola "/profile\/(.*)/" akan
mengembalikan ["profile/bertzzie", "bertzzie"], yang mana kita hanya
memerlukan "bertzzie").
Pengambilan URL pengguna sekarang dilakukan melalui fin.getCurrentPath.
Fungsi ini cukup gamblang, di mana kita hanya mengambil informasi URL
sekarang melalui location.pathname, dan kemudian melakukan pembersihan
terhadap data yang diberikan. Pembersihan yang dimaksud adalah dengan
menghapus garis miring di depan maupun belakang, serta penambahan root.
Fungsi ini dapat juga diletakkan pada MyLib.URL jika diinginkan. Kita
meletakkan fungsi ini padaMyLib.Routes dengan pertimbangan
bahwa currentPath atau pathname yang kita gunakan memiliki aturan spesifik
dari routing library kita (garis miring pembuka dan penutup harus dihapus,
rute asal, dst). Jika diletakkan pada MyLib.URL, kita pada dasarnya dapat
langsung mengembalikan location.pathname.
Terakhir, pengguna library dapat melakukan pemindahan rute melalui fungsi
publik fin.navigate, yang hanya melakukan pemanggilan Run dan kemudian
menyimpan perpindahan ini ke dalam sejarah
penelusuran browser melaluihistory.pushState. fin.navigate perlu dipisahkan
dengan Run terutama karena penyimpanan sejarah ini. Terkadang kita ingin
memanggil Run tanpa memindahkan sejarah, misalnya ketika pengguna menekan
tombol Back. Jika aksi pengguna menekan Back disimpan dalam sejarah, kita
akan memiliki perulangan sejarah sampai tidak terbatas.
Navigasi Link Otomatis
Untuk menambahkan fitur terakhir dari routing library, kita dapat membuat
sebuah fungsi yang akan mengubah semua link (elemen a pada HTML) untuk
menggunakan navigasi dari routing library kita. Beriktu adalah kode yang
digunakan:
1
2
3
4

var HrefClickHandler = function (link, r) {


return function (e) {
e.preventDefault();

r.navigate(link.href, null, null);

6
7
8
9
10

};
};

fin.init = function () {
var links = document.querySelectorAll("a"),

11
12
13
14

= 0,

len

= links.length,

that

= this,

link;

15
16

for (i = 0; i < len; i += 1) {

17

link = links[i];

18
19

link.addEventListener("click", HrefClickHandler(link, that));

20

21
22

window.onpopstate = function (e) {

23

Run(location.href);

24
25

};
};

Pertama-tama kita membuat closure HrefClickHandler yang akan menghasilkan


sebuah fungsi untuk menangani link (elemen DOM
dari a). HrefClickHandler menerima dua parameter, elemen DOM yang ingin
ditangani dan objekMyLib.Routes. Objek MyLib.Routes digunakan untuk melakukan
navigasi ke link sesuai dengan aturan yang telah kita tentukan, sementara
elemen DOM digunakan untuk referensi alamat yang akan dituju.
Method fin.init sendiri cukup sederhana: ambil semua elemen a yang ada,
kemudian tambahkan event click yang akan ditangani oleh HrefClickHandler.
Kita juga menambahkan pemantauan even popstate pada objek window, untuk
penanganan tombol Back. Hal ini berarti pengguna routing library mau tidak
mau harus melakukan pemanggilanfin.init ketika pertama kali memanggil

library. Jika anda tidak menginginkan hal ini, pantau event popstate pada
akhirconstructor.
Penutup
Pada akhir tulisan ini kita telah melihat bagaimana menggunakan History API
dengan praktis dan maksimal. Walaupun masih terdapat banyak kekurangan
dari routing library yang kita kembangkan, kita telah memanfaatkan History
API dengan efektif. Adapun beberapa kekurangan yang belum terimpelemntasi
dengan baik yaitu:
1. HrefClickHandler belum dapat menangani link eksternal. Jika tujuan link
yang diberikan oleh a adalah ke website
lain, HrefClickHandler seharusnya tahu dan tidak menanganinya
dengan MyLib.Routes.
2. Belum terdapat mekanisme atau method yang dapat digunakan ketika kita
ingin mengubah root secara dinamis.
3. MyLib.Routes belum menangani HTTP dengan baik. Misalnya, pengiriman
data form melalui POST atau PUT tidak akan dapat ditangani.
Implementasi perbaikan dari beberapa kekurangan di atas (dan pastinya masih
terdapat banyak kekurangan lain lagi) akan diserahkan sebagai latihan untuk
pembaca.
Kode lengkap dari routing library yang kita kembangkan, beserta contoh
penggunaannya dapat diambil pada halaman Github untuk artikel ini.

Catatan Kaki
[1]

Dari sudut pandang Rekayasa Perangkat Lunak (Software E

[2]

Kata kunci yang umumnya digunakan adalah Rewrite all U


[3]

[4]

Pengguna nginx dapat mencoba mengakses tutoria

Baca tentang Indirect Invocation Pattern jika tidak men

Javascript Module System


Pada bagian ini kita akan membahas proses melakukan organisasi kode program.
Ketika kita mengembangkan sebuah aplikasi kecil sederhana, organisasi kode
program bukan merupakan masalah. Aplikasi yang sederhana, yang katakanlah
hanya terdiri dari beberapa ratus baris kode, dapat disimpan di dalam sebuah
file dan masih tetap dapat dirawat dengan mudah. Namun seiring berkembangnya
aplikasi, seringkali kita akan mencapai satu titik di mana struktur program
dan fungsi-fungsi di dalamnya menjadi sulit dirawat (unmaintainable) dan
bahkan sulit dimengerti. Kode program yang awalnya mudah dimengerti dengan
fungsi-fungsi yang terfokus dan terpisah dengan baik menjadi kumpulan sayuran
yang menyerupai gado-gado: saling bercampur dan tidak kentara kegunaan dari
masing-masing komponennya.
Pada bahasa pemrograman lain, untuk menanggulangi hal ini kita melakukan dua
hal:
1. Memecah-mecah kode program menjadi banyak bagian-bagian yang lebih
kecil. Biasanya bagian kecil dari kode ini disebut dengan nama modul.

Setiap kode modul memiliki peranan spesifik, dan biasanya sebuah modul
terdiri dari beberapa fungsi. Semakin spesifik dan sejenis fungsi yang
terkumpul di dalam sebuah modul semakin baik. Tingkat kecocokan fungsifungsi di dalam modul ini kita kenal dengan istilah cohesion.
2. Setelah kode program dipecah menjadi bagian-bagian kecil, masing-masing
komponen kecil ini kemudian dapat kita hubungkan untuk menjadi sebuah
komponen yang lebih besar atau bahkan program jadi.
Pembuatan modul dari kode program dapat dilakukan dengan banyak cara, seperti
mengumpulkan fungsi-fungsi ke dalam kelas atau objek, dan kmeudian memasukkan
kode tersebut ke dalam sebuah file. Bagian kedua, di mana kita dapat
memanggil modul-modul lain dengan mudah, biasanya didukung secara langsung
oleh bahasa pemrograman yang digunakan. Contohnya, pada bahasa Java kita
memiliki perintah import dan pada C# kita memiliki perintah using.
Javascript, sampai pada saat penulisan (November 2014), sayangnya belum
mengimplemenatsikan fitur ini secara alami di dalam bahasa (meskipun telah
direncanakan untuk ke depannya).
Karena belum diimplementasikan secara langsung oleh bahasa pemrogramannya,
situasi sistem manajemen modul pada Javascript belum terlalu baik. Terdapat
beberapa alternatif implementasi, yang berbeda-beda dan memiliki kelebihan
dan kekurangannya masing-masing. Kita akan mencoba melihat dan membahas
beberapa sistem yang paling populer pada bagian ini, yaitu AMD, CommonJS, dan
terakhir ES6 Harmony untuk melihat bagaimana Javascript akan
mengimplementasikan sistem manajemen modul kedepannya.
Persiapan Module System: Script Loader
Sebelum mulai membahas tentang module system, kita akan terlebih dahulu
membahas sebuah sistem lain yang akan selalu digunakan oleh module
system: script loader. Script loader merupakan sebuah sistem yang membantu
kita dalam melakukan pengambilan file-file kode yang dibutuhkan oleh module
system.
Karena terdapat banyak jenis dari script loader, masing-masing dengan
kelebihan dan kekurangannya sendiri, kita tidak akan membahas hal ini terlalu
dalam. Script loader yang akan kita gunakan adalah RequireJS.
AMD (Asynchronous Module Definition)

Tujuan dari pembuatan AMD adalah untuk menyediakan sebuah solusi dalam
mengembangkan kode modular Javascript. AMD dirancang agar kita dapat
mendefinisikan dan mengunakan modul secara asinkron. Sifat asinkron serta
fleksibilitas dari AMD membuat AMD banyak disukai oleh pengembang, dan
dianggap sebagai standar implementasi module system yang cukup baik sebelum
ES Harmony siap digunakan.
Module System pada AMD
Terdapat dua method utama yang perlu kita mengerti sebelu menggunakan AMD,
yaitu:
1. define digunakan untuk membuat sebuah model baru.
2. require digunakan untuk menggunakan modul yang telah didefinisikan,
menjadikan modul kita sekarang bergantung kepada modul lain.
Fungsi define dapat digunakan dengan cukup sederhana:
1

define(

module_id /*optional*/,

[dependencies] /*optional*/,

definition function /*function for instantiating the module or object*/

);

Seperti yang dapat dilihat dari kode di atas, bagian module_id bersifat
opsional, dan biasanya hanya digunakan ketika kita menggunakan sistem lain
yang tidak mendukung AMD. Jika module_id tidak diisikan, modul yang kita
gunakan disebut dengan modul anonim. Jika modul kita bersifat anonim, maka
nama dari modul akan dikenali sebagai nama file oleh AMD. Hal ini
memungkinkan kita untuk mengubah lokasi modul dengan bebas, tanpa harus
terikat dengan bagaimana kita menyimpan modul tersebut.
Parameter kedua, [dependencies], juga bersifat opsional. Parameter ini
merupakan sebuah array dari modul-modul lain yang akan digunakan di dalam
modul yang akan dibuat. Karena seringkali kita akan membuat modul yang tidak
bergantung dengan modul-modul lain, maka modul ini bersifat opsional.

Argumen ketiga (definition function) merupakan fungsi yang nantinya


dijalankan untuk menghasilkan modul baru kita. Bayangkan fungsi ini sebagai
constructor dari objek yang akan kita gunakan nantinya.
Berikut adalah contoh pembuatan sebuah modul sederhana:
1

define('myModule',

['foo', 'bar'],

// fungsi definisi modul

// perhatikan bahwa foo dan bar, dependensi dari modul,

// dijadikan parameter fungsi di sini.

function ( foo, bar ) {

// modul ini akan dikembalikan nantinya

// agar dapat digunakan oleh pengguna modul lain

var myModule = {

10

doStuff:function(){

11

console.log('Yay! Stuff');

12

13

14
15

return myModule;

16
17

}
);

Penggunaan modul sendiri dapat dilakukan dengan cukup sederhana:


1

require(['foo', 'bar'], function ( foo, bar ) {

// kode di sini

3
4
5

foo.doSomething();
});

Pada kode di atas, kita dapat menganggap foo dan bar sebagai sebuah fungsi
eksternal. Untuk menggunakan kedua fungsi ini kita memanggilnya melalui

parameter pertama dari require, dan kemudian mengirimkan kedua objeknya


melalui parameter fungsi. Cara ini merupakan bentuk paling sederhana dari
penggunaan modul pada AMD.
CommonJS
CommonJS merupakan standar yang lebih luas dari AMD, di mana ia mencakup
tidak hanya module system, tetapi juga paket-paket standar yang ada di dalam
Javascript. Kita dapat menyamakan CommonJS dengan pustaka standar pada bahasa
seperti Java atau C# (misal: system.out.* pada Java). Hal ini berarti selain
cara untuk membuat dan memanggil modul CommonJS juga mencakup berbagai modul
standar seperti modul input output, pembacaan file, dll.
Seperti AMD, CommonJS memiliki dua komponen utama untuk melakukan pembuatan /
pemanggilan modul:
1. exports merupakan sebuah objek global yang digunakan untuk menyimpan
modul.
2. require merupakan sebuah fungsi global yang dapat memanggil modul-modul
yang ada di dalam exports.
Langsung saja, berikut adalah contoh pembuatan modul pada CommonJS:
1
2
3
4

// definisi fungsi atau objek atau modul


// yang ingin kita buat
function foobar(){
this.foo = function(){

console.log('Hello foo');

};

7
8

this.bar = function(){

console.log('Hello bar');

10
11
12
13
14

};
};

// simpan agar fungsi foobar dapat digunakan

15

// di dalam modul lain


exports.foobar = foobar;

Seperti yang dapat dilihat pada kode di atas, pada CommonJS kita cukup hanya
menyimpan fungsi atau objek yang kita ingin gunakan pada modul lain ke
dalam exports. exports sendiri bersifat global, yang artinya dapat kita
panggil kapanpun, di manapun.
Untuk menggunakan fungsi foobar di atas kita dapat memanggil
fungsi require seperti berikut:
1

var foobar = require('./foobar').foobar,

test = new foobar();

3
4

test.bar(); // 'Hello bar'

Mirip seperti AMD, ketika kita memanggil modul dari CommonJS, pemanggilan
dilakukan melalui file, relatif terhadap modul yang sedang mengakses
sekarang. Jika dianalogikan, require('./foobar') pada kode di atas dapat
dianggap mengakses objek global exports yang ada pada file foobar.js.
Perbandingan AMD dan CommonJS
Sekilas CommonJS memang terlihat lebih sederhana untuk digunakan. Sayangnya,
terdapat banyak pengembang yang berpendapat bahwa CommonJS lebih cocok
digunakan hanya pada sisi server. Pendapat seperti ini didasarkan pada
berbagai modul bawaan pada CommonJS yang dianggap lebih condong ke penggunaan
Javascript pada sisi server misalnya I/O, system (pemanggilan program,
pembuatan proses, dkk). Fitur-fitur seperti ini tentu saja mustahil untuk
diimplementasikan pada sisi browser tanpa membuka lobang keamanan yang besar.
Hal ini kemudian menjadikan CommonJS gagal sebagai stanar de facto untuk
module system JavaScript, meskipun CommonJS digunakan secara luas pada
pemrograman sisi server seperti NodeJS.
AMD sendiri lebih menggunakan pendekatan yang mengutamakan browser dalam
pengembangannya. Fitur-fitur seperti pengambilan modul secara asinkron dan
kompatibilitas terhadap Javascript lama menunjukkan hal ini. Tanpa
menggunakan I/O file, AMD dapat mendukung berbagai jenis modul, dari pustaka

seperti jQuery sampai framework seperti Dojo. Semua jenis modul AMD juga
dapat dipastikan berjalan secara alami (native) dalam browser. Hal ini
membuat AMD menjadi sangat fleksibel.
Pendekatan module system dari ES Harmony sendiri sebenarnya lebih dekat
dengan CommonJS dalam beberapa hal seperti cara impor / ekspor modul
dibandingkan dengan AMD. Hal ini dikarenakan ES Harmony yang mencoba untuk
mengikutsertakan penggunaan Javascript pada server dan klien sebagai kasus
penggunaan module systemnya. Tanpa adanya fungsi standar yang berhubungan
dengan server, ES Harmony juga mendukung berbagai fitur baik dari AMD seperti
penggunaan modul asinkron.
Secara singkat, beberapa kelebihan utama dari AMD yaitu:

Pendekatan modul yang berjalan secara native di browser.

Fleksibel dan cukup sederhana untuk digunakan.

Definisi modul dibungkus dengan baik, tanpa harus memenuhi (mengotori)


objek global.

Dapat bekerja secara asinkron.

Tidak bergantung kepada sisi server.

Karena bersifat asinkron, kita dapat melakukan lazy loading jika


diperlukan.

Adapun kelebihan utama dari CommonJS adalah:

Memiliki banyak fungsi bawaan yang berguna.

Karena menggunakan sisi server, terdapat banyak fitur yang tidak


mungkin dimiliki implementasi lain.

Cara kerjanya lebih dekat dengan ES Harmony.

Lebih sederhana digunakan dibandingkan dengan AMD (masih dapat


diperdebatkan).

ES6 Harmony

Setelah melihat bagaimana AMD dan CommonJS mencoba menyelesaikan permasalahan


module system pada Javascript, sekarang kita akan melihat bagaimana
Javascript berencana untuk menyelesaikan masalah ini. Pada ES Harmony, konsep
dasar yang diguanakan untuk menyelesaikan masalah module system masih sama:
1. Perintah import mengikatkan nilai export dari sebuah modul untuk
digunakan secara lokal.
2. Perintah export membuat nilai-nilai yang ada di dalam sebuah modul
dapat dibaca (hanya baca, tidak dapat mengubah) oleh modul lain.
Perbedaan utama dari sistem yang disesiakan ES Harmony hanyalah pada
integrasi yang lebih ketat karena sifat native-nya. Tidak terdapat perbedaan
yang terlalu banyak dibandingkan AMD, CommonJS, dan bahkan bahasa pemrograman
lain pada implemenatsi module system ES Harmony. Faktanya, implementasi yang
dibuat sangat umum dan sederhana sehingga kebanyakan programmer akan langsung
dapat mengerti maksud dan cara penggunaan kode module system ES Harmony,
misalnya seperti berikut:
1
2

module staff{
// spesifikasikan export untuk nilai

// yang dapat diakses modul lain

export var baker = {

bake: function( item ){

console.log('Woo! I just baked ' + item);

8
9
10
11
12

}
}

module skills{
export var specialty = "baking";

13
14
15
16
17
18

export var experience = "5 years";


}

module cakeFactory{

// hanya import bagian tertentu saja

19

import baker from staff;

20
21

// import semuanya

22

import * from skills;

23
24

export var oven = {

25

makeCupcake: function( toppings ){

26

baker.bake('cupcake', toppings);

27

},

28

makeMuffin: function( mSize ){

29

baker.bake('muffin', size);

30

31

32

Kita bahkan dapat menggunakan modul yang berada pada server lain dengan ES
Harmony, seperti berikut:
1

module cakeFactory from 'http://addyosmani.com/factory/cakes.js';

cakeFactory.oven.makeCupcake('sprinkles');

cakeFactory.oven.makeMuffin('large');

Penutup
Pada bagian ini kita telah melihat beberapa pilihan moduel system pada
Javascript. Module system memiliki kelebihan jika dibandingkan dengan
penggunaan fungsi maupun objek global pada Javascript yang biasa kita
gunakan. Keuntungan utamanya yang didapatkan misalnya kita tidak lagi perlu
mengkhawatirkan urutan pemasukan file pada tag <script>yang digunakan.
Pembaca sangat direkomendasikan untuk setidaknya mencoba salah satu dari
CommonJS atau AMD sebelum melanjutkan ke bab berikutnya, karena pada bagian
framework kita akan menggunakan module system sepenuhnya.

Framework Javascript
Pada bagian ini kita akan melihat apa yang dimaksud dengan Framework secara
umum, serta berbagai contoh framework yang ada pada Javascript. Tujuan utama
kita mempelajari ini adalah karena pada pengembangan aplikasi yang berukuran
signifikan (katakanlah, lebih dari 1.000 baris), umumnya kita akan menemui
banyak kode-kode yang serupa dengan beberapa parameter atau nilai berbeda.
Ketika hal ini terjadi, kita perlu mengembangkan apa yang dikenal dengan
abstraksi. Mengutip Wikipedia:
Pada ilmu komputer, abstraksi merupakan proses memisahkan inti pemikiran
(ide; gagasan) dari contoh (kejadian) spesifik ketika gagasan tersebut
diimplementasikan.
Struktur komputasi dari gagasan didefinisikan melalui makna (semantik)
mereka, dengan menyembunyikan detil dari cara kerja gagasan tersebut.
- Abstraction (Computer Science)
Ketika mengembangkan aplikasi yang relatif besar, pada umumnya kita akan
menemukan bagian dari aplikasi yang berulang. Misalnya, sebuah aplikasi yang
perlu mengakses basis data akan memiliki kode untuk melakukan koneksi dan
pengiriman perintah ke basis data. Meskipun perintah-perintah yang dikirimkan
berbeda, umumnya langkah untuk mengirimkan perintah tidak jauh berbeda:
1. Koneksi ke basis data
2. Kirimkan perintah
3. Proses respon
Ketiga langkah di atas dapat kita anggap sebagai implementasi dari sebuah
gagasan (inti pemikiran) umum. Inti pemikiran yang ada pada langkah di atas
adalah berhubungan dengan basis data. Karena hal ini akan dilakukan berulang
kali dalam sebuah aplikasi, akan sangat baik jika kita memiliki sebuah
gagasan umum yang membungkus ketiga langkah di atas menjadi hanya kirim

perintah ke basis data tanpa perlu memikirkan detil langkahnya. Dengan


begitu, makna semantik dari apa yang kita lakukan (kirim perintah ke basis
data) tidak perlu terganggu oleh detil implementasi. Hal inilah yang kita
kenal sebagai abstraksi dalam ilmu komputer.
Abstraksi memiliki banyak kelebihan:
1. Karena gagasan inti telah terpisah dari detil implementasi, secara
teori kita dapat mengganti detil implementasi tanpa perlu mengganti
gagasan inti. Pada contoh di atas misalnya, kita dapat mengganti basis
data (misal: dari MySQL ke Postgres) tanpa perlu mengganti kode-kode
lain yang menggunakan abstraksi kita. Kode yang diganti hanya kode
abstraksi yang menangani detil implementasi hubungan ke basis data.
2. Abstraksi umumnya membuka peluang untuk mengembangkan sebuah antarmuka
pemrograman yang sangat ekspresif dan kuat. Misalnya, dengan abstraksi
kita dapat memperluas sebuah gagasan (dari kirim perintah ke basis
data menjadi akses data) sehingga aplikasi yang dikembangkan di atas
abstraksi ini dapat memiliki interaksi dengan data yang berbeda tanpa
perlu tahu apa saja data tersebut. Sebagian data dapat datang dari
basis data, dan lainnya datang dari XML, tetapi aplikasi kita cukup
tahu bahwa data tersebut ada dan dapat diakses, tanpa perlu tau cara
membaca XML atau basis data.
3. Karena poin kedua, kompleksitas kode dari aplikasi akan otomatis
menurun. Ketika kita membangun di atas abstraksi, sebagai pengguna
abstraksi terdapat banyak detil implementasi yang tidak perlu kita
pikirkan, menyebabkan implementasi aplikasi menjadi lebih sederhana.
Hal ini secara otomatis akan mempermudah kita dalam mengembangkan
aplikasi.
Meskipun dapat sangat membantu kita dalam mengembangkan aplikasi, abstraksi
yang tidak diimplementasikan dengan hati-hati juga dapat menyebabkan hal
buruk. Ketika kita menggunakan sebuah abstraksi yang dikembangkan oleh orang
lain, tentunya kita harus mempelajari detil abstraksi tersebut sebelum dapat
menggunakannya dengan efektif. Hal ini seringkali menjadi sandungan dan
memperpanjang waktu pengembangan. Sebuah abstraksi yang tidak tepat juga
bahkan dapat mempersulit pengembangan aplikasi kita. Detil mengenai abstraksi
yang baik dan buruk sulit dijelaskan tanpa berpengalaman langsung bekerja
dengan abstraksi terlebih dahulu. Karena hal ini, kita akan membahas
kelebihan dan kekurangan abstraksi ini sembari mempelajari berbagai abstraksi
yang ada pada bagian-bagian selanjutnya.

Sebelum mulai masuk dan mencoba berbagai abstraksi yang ada, kita akan
melihat jenis-jenis abstraksi yang ditawarkan oleh lingkungan Javascript
terlebih dahulu. Secara umum, di lingkungan Javascript terdapat beberapa
jenis abstraksi yang dapat kita temui:
1. Library
2. Widget Toolkit
3. Framework
Mari kita lihat perbedaan dari masing-masing abstraksi ini.
Library
Sebuah library, atau pustaka kode, didefinisikan sebagai berikut pada tulisan
ini:
Kumpulan implementasi fungsi-fungsi umum dari perangkat lunak yang memiliki
perilaku spesifik
Fungsi yang dimaksudkan pada kumpulan implementasi fungsi-fungsi umum di
atas adalah fungsi dalam arti fungsionalitas, bukan fitur function pada
sebuah bahasa pemrograman. Sebuah library dapat menyediakan kumpulan
implementasi fungsionalitas untuk membaca file PDF atau melakukan
kalkulasi Fast Fourier Transform. Fitur bahasa yang digunakan
oleh library untuk mendapatkan sebuah fungsionalitas tertentu mungkin
saja function, class, atau yang lain. Cara library mengimplementasikan
sesuatu tidak terlalu penting, selama fungsionalitas yang diinginkan dapat
tercapai.
Fungsi-fungsi umum yang ada di dalam sebuah library umumnya memiliki perilaku
spesifik. Perilaku spesifik ini diartikan sebagai sebuah spesifikasi masukan
dan keluaran dari fungsi tersebut. Spesifikasi ini dapat mencakup tipe data
(masukan maupun keluaran), efek samping, paramter fungsi, nilai kembalian,
dan banyak hal lain. Tingkat kelengkapan spesifikasi dari
sebuah library sendiri berbeda-beda, dan tentunya semakin lengkap (spesifik)
detil spesifikasinya semakin baik pulalibrary tersebut.
Untuk memperjelas lagi, mari kita lihat contoh penggunaan sebuah library pada
aplikasi. Diagram berikut menunjukkan alur arus data sebuah aplikasi yang
menggunakan library:

Pada diagram di atas, kita ingin mengembangkan sebuah aplikasi yang dapat
memainkan file musik ogg vorbis. Terdapat dua buah library yang digunakan
oleh aplikasi kita, yaitu libvorbisfile dan libalsa. Alur kerja dari aplikasi
adalah kira-kira seperti berikut:
1. Pengguna memberikan file ogg kepada aplikasi.
2. Aplikasi memberikan file ogg kepada libvorbisfile.
3. libvorbisfile memproses file ogg dan memberikan kembali hasil proses
berupa stream audio ke aplikasi.
4. Aplikasi memberikan stream audio dari libvorbisfile ke libalsa.

5. libalsa memainkan stream audio tersebut dan mengeluarkan suaranya ke


perangkat audio seperti speaker.
Perhatikan bagaimana dengan menggunakan libvorbisfile dan libalsa aplikasi
kita tidak perlu lagi mengetahui cara mengubah file ogg menjadi stream audio
dan cara mengubah stream audio menjadi suara. Kita hanya mengirimkan data
yang dibutuhkan libvorbisfile maupun libalsa sesuai dengan spesifikasi
mereka, dan mendapatkan keluaran yang kita inginkan.
libvorbisfile sendiri, seperti yang dapat dilihat pada diagram di atas,
menggunakan dua buah library lain lagi,
yaitulibogg dan libvorbis. libogg digunakan untuk mengubah
file ogg menjadi vorbis stream, yang merupakan formatstream spesifik untuk
vorbis. Keluaran dari libogg ini kemudian diberikan kepada libvorbis untuk
diubah menjadistream audio standar. Penggunaan library lain di dalam
sebuah library ini merupakan hal yang sangat umum terjadi, dan pada akhirnya
tidak mengherankan jika aplikasi yang kita kembangkan akan memerlukan
banyak library.
Berdasarkan penuturan di atas, kita dapat menyimpulkan beberapa hal
tentang library:
1. Library merupakan kumpulan fungsi, yang kemudian dapat digunakan untuk
membangun aplikasi. Dari sudut pandang lain, kita dapat
menganggap library sebagai sebuah blok fungsi seperti lego yang dapat
digabungkan untuk menjadi sebuah bentuk (dalam kasus kita: apikasi
atau library yang lebih kompleks).
2. Sebuah library dapat digunakan dengan bebas, sesuai dengan kebutuhan.
Jika tidak ingin menggunakan sebuahlibrary, kita tetap dapat
mengimplementasikan fungsi yang diinginkan. Ketika ingin
menggunakan library pun, kita tidak perlu harus menggunakan semua
fungsionaltias yang disedikaan library tersebut.
3. Sebuah library yang baik biasanya menyediakan fungsi-fungsi yang
sejenis dan saling berhubungan. Prinsip ini kita kenal dengan
istilah *cohesion*. Semakin terfokus fungsi-fungsi yang ada di dalam
sebuah library semakin baik. Memiliki fungsi untuk menghitung luas
lingkaran di dalam library untuk memainkan file mp3 misalnya, tidak
akan menambahkan manfaat apapun selain beban pengujian dan perawatan
kode.

4. Sebuah library relatif mudah digunakan bersamaan dengan library lain


karenanya kita dapat dengan mudah menggunakan beberapa library dalam
satu aplikasi. Kendatipun terdapat library yang bergantung
kepada library lain (i.e. kita tidak dapat menggunakan library A tanpa
juga menggunakan library B), hal ini umumnya dihindari. Tingkat
ketergantungan antar library kita kenal dengan istilah coupling, dan
semakin rendah tingkat ketergantungan (coupling) semakin baik.
Contoh dari sebuah library pada lingkungan Javascript adalah jQuery. jQuery
menawarkan berbagai fungsionalitas umum yang biasanya digunakan pengguna
Javascript di dalam sebuah abstraksi sederhana yang mudah digunakan.
Misalnya, jika kita ingin menggunakan XHR, alih-alih menuliskan kode panjang
agar XHR dapat berjalan di semua browser, jQuery menawarkan fungsi .ajax yang
sederhana:
1

$.ajax({

url: "/api/getWeather",

data: {

zipcode: 97201

},

success: function(data) {

$("#weather-temp").html("<b>" + data + "</b> degrees");

8
9

}
});

Widget Toolkit
Setingkat di atas library, kita memiliki Widget Toolkit dalam dunia
Javascript. Definisi sederhananya adalah:
(Kumpulan) library untuk membuat elemen-elemen tertentu untuk mengembangkan
antarmuka (UI)
Widget Toolkit merupakan kumpulan library, atau terkadang
sebuah library besar yang dibangun di atas banyak librarykecil lainnya.
Karena merupakan kumpulan library, Widget Toolkit biasanya menspesifikasikan
bagaimana seluruh libraryyang digunakan saling berinteraksi. Hal ini berarti
terdapat coupling antara semua library yang digunakan oleh Widget Toolkit.

Sebuah Widget Toolkit seringkali juga bukan hanya menggunakan library sebagai
alat untuk membangun antarmuka pengguna, tetapi juga untuk membantu fiturfitur tertentu. Misalnya, sebuah Widget Toolkit dapat menggunakan jQuery
untuk berinteraksi dengan elemen-elemen antarmuka pengguna yang
disediakannya.
Karena sebuah antarmuka biasanya disertai dengan berbagai komponen pendukung
seperti ikon atau gambar latar, sebuah Widget Toolkit seringkali dipaketkan
bersamaan dengan komponen-komponen pendukung tersebut. Dalam lingkungan
Javascript, komponen-komponen pendukung ini dapat berupa CSS, gambar, font,
suara, dst.
Sebagai suatu abstraksi yang mencampurkan berbagai library dan komponen
antarmuka, sebuah Widget Toolkit biasanya memiliki cara kerja dan interaksi
antar komponen yang cukup spesifik. Hal ini menyebabkan Widget Toolkit sulit
digunakan bersamaan dengan Widget Toolkit atau bahkan library lain. Ketika
memutuskan untuk menggunakan sebuah Widget Toolkit, biasanya evaluasi akan
dilakukan dengan lebih hati-hati, karena pergantian Widget Toolkit biasanya
akan diikuti oleh pergantian seluruh komponen antarmuka beserta
dengan library yang digunakan.
Untuk memperjelas perbedaan antar Widget Toolkit ini, mari kita lihat sekilas
bagaimana tiga buah Widget Toolkit yang cukup populer (jQuery UI, Dojo
Toolkit, YUI) menampilkan sebuah widget untuk memilih tanggal (datepicker):
Kode (jQuery UI)

Tampilan

Kode (Dojo)

Kode (YUI)

Tampilan

Tampilan

Dari ketiga Widget Toolkit yang ada di atas, kita dapat melihat bagaimana
baik tampilan hasil antarmuka maupun kode yang digunakan untuk menghasilkan
elemen tersebut sangat berbeda. Perbedaan paling mencolok tampak pada
tampilan yang dihasilkan. Karena kita tidak menambahkan CSS sendiri di sini,
tampilan yang dihasilkan didapatkan dari masing-masing CSS yang telah
dipaketkan oleh Widget Toolkit. Cara pembuatan elemen juga berbeda-beda. Pada
jQuery UI, kita dapat melihat bagaimana elemen dibuat dari pengembangan

elemen DOM hasil pengambilan melalui $("#datepicker"), sementara pada Dojo


kita langsung mengubah elemen HTML-nya.
Karena perbedaan cara kerja seperti ini, kita tidak dapat dengan mudah
langsung saling menggabungkan ketiga Widget Toolkit ini. Belum lagi jika pada
CSS yang dipaketkan terdapat styling terhadap elemen yang sama. Hal ini lah
yang terutama membedakan antara Widget Toolkit dengan library.
Framework
Framework dalam konteks perangkat lunak dapat kita definisikan sebagai
berikut:
Lingkungan pengembangan perangkat lunak yang menyediakan fasilitas
spesifik untuk membuat sistem baru
Sebuah framework biasanya menyediakan abstraksi yang memberikan berbagai
fungsi generik. Fungsi-fungsi generik ini kemudian dapat kita ubah atau kita
lengkapi untuk mengembangkan sebuah sistem baru. Dengan melengkapi dan
mengubah sistem ini dengan kode kita sendiri, idealnya kita dapat
menghasilkan sebuah aplikasi baru sesuai dengan kebutuhan.
Sebuah framework bukan hanya menyediakan fungsi seperti library, tetapi juga
sebuah lingkungan pegnembangan. Sebuah lingkungan pengembangan biasanya
disediakan framework untuk memfasilitasi kita dalam mengembangkan apliaksi.
Lingkungan pengembangan dapat saja mengikut sertakan compiler, sekumpulan
aplikasi pendukung, library, dan berbagai perangkat lainnya yang dapat saling
bekerja sama untuk membangun sebuah solusi.
Karena banyaknya komponen dalam sebuah framework ini,
sebuah framework biasanya lebih dogmatis dalam memandang cara pengembangan
perangkat lunak. Dari metode interaksi sampai dengan penamaan file tidak
jarang ditentukan olehframework. Sebagai pengguna framework, biasanya kita
hanya mengikuti aturan yang telah ditentukan oleh frameork, dan bekerja di
dalam lingkup framework tersebut. Hal ini juga menyebabkan kita sulit
menggunakan sebuah framework denganframework lainnya secara bersamaan.
Sebuah framework memiliki beberapa ciri khas yang membedakannya
dengan library pada umumnya:
1. Inversion of Control, yaitu keadaan di mana alur kerja dari aplikasi
kita dikontrol oleh framework, bukan penggunaframeowrk.

2. Standard, di mana sebuah framework memiliki berbagai perilaku dan alur


standar, dari nama file sampai alur program. Hal ini menyebabkan
pengguna framework yang tidak mengikuti perkembangan awal aplikasi
dapat dengan mudah terjun ke dalam pengembangan kapanpun.
3. Extensibility, yaitu framework dapat dikembangkan atau diubah melalui
mekanisme yang telah disediakan, biasnya plugin.
Spesifik terhadap dunia pengembangan web, terdapat beberapa
jenis framework yang umum kita temui, yaitu:
1. MVC (Model-View-Controller), merupakan framework yang membagi
arsitektur aplikasi ke dalam 3 bagian: Model (data), View (antarmuka),
dan Controller (aturan bisnis, penghubung Model dan View).
2. Pull (Component) Based Framework, merupakan framework yang berbasis
antarmuka, di mana terdapat komponen antarmuka dari framework yang
kemudian menarik data dari server (kontras dengan framework umum yang
mengirimkan antarmuka bersamaan dengan data).
3. n-tier Framework, framework yang membagi-bagi aplikasi ke dalam banyak
lapisan. Ukuran dan kegunaan dari lapisan sendiri tidak dibatasi.
Meskipun terdapat banyak jenis framework, pada dasarnya tujuan utama
dari framework adalah untuk memfasilitasi pengembang (pengguna framework)
fokus dalam mengembangkan aplikasi. Dengan menyediakan lingkungan
pengembangan dan berbagai komponen, sebuah fraemwork melepaskan kita dari
berbagai detil implementasi pendukung, sehingga kita dapat berfokus pada
aturan bisnis dan kebutuhan perangkat lunak yang ada.
Contohnya pada sebuah framework pengembangan web, umumnya akan terdapat
komponen dan mekanisme khusus untuk manajemen pengguna. Hal ini dapat
membebaskan pengembang dari kode untuk menyimpan dan mengambil statepengguna,
yang pada kebanyakan aplikasi adalah sama. Dengan menghindari repetisi
seperti ini kita dapat berfokus kepada fitur-fitur yang ada pada kebutuhan
perangkat lunak kita.

React JS (Pengenalan)
ReactJS merupakan sebuah library dari facebook yang dapat digunakan untuk
membangun antarmuka pengguna. Pada bagian ini kita akan mencoba menggunakan
React untuk membangun aplikasi sederhana, untuk melihat konsep-konsep inti
yang ditawarkan oleh React.
Sebelum mulai mencoba menggunakan React, beberapa prasyarat yang diperlukan
yaitu:
1. React Starter Kit untuk berbagai file dari React
2. jQuery, sebagai library pendukung

Jika sudah mengambil kedua kebutuhan di atas, ekstrak React Starter Kit ke
dalam sebuah direktori dan jalankan sebuah web server lokal (misal: httpserver) di dalamnya. Seluruh eksperimen kita akan dijalankan dari direktori
ini.
Hello, React!
Untuk memulai eksperimen, kita akan langsung mencoba membuat sebuah halaman
sederhana dengan menggunakan ReactJS. Buat sebuah file dengan
nama index.html di dalam direktori utama starter kit ReactJS, dan isikan
dengan kode berikut:
1

<!DOCTYPE html>

<html>

<head>

<script src="build/react.js"></script>

<script src="build/JSXTransformer.js"></script>

</head>

<body>

<div id="content"></div>

<script type="text/jsx">

10

React.render(

11

<h1>Hello, world!</h1>,

12

document.getElementById('content')

13

);

14
15
16

</script>
</body>
</html>

Buka index.html pada browser dan lihat hasil eksekusinya.

Apa yang baru saja kita lakukan?


Pada bagian <head> dari index.html kita memasukkan semua modul Javascript
yang dibutuhkan oleh ReactJS.<div id="content"> merupakan elemen yang akan
menampung antarmuka yang kita hasilkan dengan menggunakan
ReactJS. React.render merupakan fungsi utama dari ReactJS, yang akan
menggambarkan antarmuka yang kita buat. Fungsi ini menerima dua buah argumen:
1. Argumen pertama merupakan bentuk dari keluaran antarmuka yang ingin
kita buat. Perhatikan bagaimana kita menggunakan sintaks
yang mirip dengan HTML di sini. Kode <h1>Hello, world!</h1> yang kita
isikan bukanHTML, melainkan sebuah sintaks khusus yang bernama JSX.
Perhatikan juga bahwa kita menggunakan<script type="text/jsx"> pada

awal tag <script>. Penjelasan lebih lanjut mengenai JSX akan kita
tambahkan nanti.
2. Parameter kedua menerima elemen DOM tempat kita ingin menampilkan
keluaran dari elemen pertama. Parameter ini cukup gamblang, dan kita
hanya mengirimkan elemen DOM standar yang didapat
daridocument.getElementById di sini.
Bentuk awal dari penggunaan ReactJS memang cukup sederhana. Kita akan melihat
bagaimana hampir keseluruhan pengembangan dari ReactJS berdasar dari model
sederhana ini nantinya.
Kode ReactJS pada File Terpisah
Jika diinginkan, kita juga dapat memisahkan kode JSX kita pada sebuah file
terpisah, layaknya kita memisahkan kode javascript biasa pada file lain.
Misalnya, kita dapat membuat sebuah file src/helloworld.js dengan isi:
1

React.render(

<h1>Hello, world!</h1>,

document.getElementById('content')

);

Dan tentunya kita harus menggantikan tag script pada index.html menjadi:
1

<script type="text/jsx" src="src/helloworld.js"></script>

Dapat dilihat bagaimana tidak terdapat perbedaan mencolok antara penggunaan


javascript dengan JSX, selain pada bagian di mana JSX dapat menerima kode
yang mirip HTML di dalamnya.
Kompilasi JSX ke Javascript
Jika ingin, kita dapat juga melakukan kompilasi JSX ke javascript, yang pada
kode sebelumnya dilakukan olehJSXTransformer.js secara otomatis dan langsung.
Kompilasi tentunya akan menghemat waktu eksekusi kode, karenabrowser dapat
langsung membaca kode javascript yang kita berikan tanpa perlu melakukan
transformasi dari JSX ke javascript lagi. Kompilasi dari JSX ke javascript

dapat dilakukan dengan menggunakan tools yang telah disediakan ReactJS,


melalui platform NodeJS.
Untuk dapat melakukan kompilasi, pertama-tama kita harus melakukan
instalasi tools:
1

npm install -g react-tools

Kita kemudian dapat mengubah src/hellworld.js yang berisi JSX menjadi


javascript dengan perintah:
1

jsx --watch src/ out/

Tentunya karena kita telah memiliki hasil kompilasi JSX, kita tidak lagi
perlu menggunakan JSXTransformer.js pada HTML kita, dan kita juga dapat
langsung memasukkan hasil kompilasi seperti berikut:
1

<!DOCTYPE html>

<html>

<head>

<script src="build/react.js"></script>

<!-- Tidak ada lagi JSXTransformer.js -->

</head>

<body>

<div id="content"></div>

<script src="out/helloworld.js"></script>

10
11

</body>
</html>

Perhatikan juga bagaimana kita tidak lagi memasukkan type="text/jsx" pada


tag <script>. Isi dariout/helloworld.js sendiri adalah seperti berikut:
1
2

React.render(

React.createElement('h1', null, 'Hello, world!'),

3
4

document.getElementById('content')
);

Dengan hanya perbedaan pada bagian sintaks JSX saja. Tentunya kita juga dapat
langsung menggunakan pemanggilan fungsi seperti yang ada pada file hasil
kompilasi ini jika mau.
Pembangunan Elemen Antarmuka ReactJS
ReactJS merupaakn sebuah library yang berbasis komponen. Hal ini menyebabkan
kita harus mengembangkan sebuah elemen antarmuka komponen demi komponen. Kita
akan langsung melihat bagaimana kita dapat mengembangkan sebuah komponen
ReactJS sederhana dengan menggunakan fungsi-fungsi yang ada.
Aplikasi percobaan yang akan kita bangun adalah sebuah todo list (TDL)
sederhana, yang menyimpan catatan berbagai hal yang akan kita lakukan.
Tampilan aplikasi sangat sederhana:

TDL yang akan kita kembangkan ini akan terdiri dari tiga buah komponen, yaitu
1. komponen yang menerima masukan pengguna untuk menambahkan data baru,
2. komponen yang menampilkan semua tugas pengguna yang telah tercatat
dalam sistem, dan
3. komponen utama TDL yang menampung kedua komponen sebelumnya.
Jika diilustrasikan, pembagian komponen kita dapat dilihat seperti gambar
berikut:

Langsung saja, kita dapat mulai membangun elemen antarmuka TDL ini dari file
HTML yang diperlukan. Buat sebuah filetodo.html dan isikan dengan kode
berikut:
1
2
3
4
5
6
7

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Todolist React</title>
</head>

<body>
<div id="content"></div>

9
10

<script src="build/react.js"></script>

11

<script src="build/JSXtransformer.js"></script>

12
13
14
15

<script type="text/jsx" src="src/todolist.js"></script>


</body>
</html>

HTML yang dibuat cukup standar. Sekarang kita akan membuat file javascript
yang dibutuhkan, yaitu src/todolist.js:
1

var TodoForm = React.createClass({

render: function () {

return (

<div className="todoForm">

<label htmlFor="todoInput">

Tambahkan Data:

</label>

<input type="text" id="todoInput" className="inputForm" />

</div>

10

);

11
12

}
});

13
14

React.render(

15

<TodoForm />,

16

document.getElementById("content")

17

);

Jalankan kode dan lihat apa yang kita dapatkan:

Apa yang baru saja terjadi?


Pertama, kita membuat sebuah komponen baru di dalam ReactJS dengan
menggunakan method React.createClass.Method ini menerima sebuah parameter
berupa objek yang dapat memiliki banyak method. Untuk contoh awal ini, kita
hanya mengirimkan satu buah method: render. render merupakan method yang
mengembalikan komponen React yang nantinya akan diubah menjadi HTML.
Elemen <div className="todoForm"> yang kita kembalikan bukan merupakan elemen
DOM murni yang dikenali langsung oleh browser. Sebagai kode JSX, kita dapat
menganggap elemen ini merupakan elemen DOM khusus milik React, yang nantinya
akan diterjemahkan menjadi HTML. Perhatikan bagaimana untuk menambahkan kelas
pada elemen kita menggunakan atribut className, bukan class seperti pada HTML

standar. Hasil kembalian fungsi ini akan diubah menjadi HTML


oleh React.render pada waktunya, dan hasil keluaran HTML ini relatif aman.
React tidak menghasilkan string HTML sehingga kita terlindungi dari serangan
XSS.
Setiap kali kita telah membuat komponen baru melalui React.createClass, kita
kemudian dapat menyimpan komponen ini ke dalam sebuah variabel. Nama dari
variabel ini kemudian akan menjadi komponen baru dari React, yang dapat
digunakan sebagai markup buatan sendiri dalam JSX. Karena hal ini lah kita
dapat langsung mengirimkan komponen yang baru kita buat
sebagai <TodoForm /> ke React.render. Kita bahkan dapat
menggunakan <TodoForm /> di dalam nilai kembalian komponen lainnya.
Untuk mengirimkan data ke dalam komponen React, kita juga dapat menambahkan
atribut ke dalam elemen yang baru saja kita buat. Atribut ini dapat diisikan
dengan nama apapun, selama tidak bertabrakan dengan atribut standar HTML.
Pengisian atribut ke dalam elemen dilakukan dengan mengguankan pemisah {},
seperti berikut:
1

var judul = "Tambahkan Data";

2
3

React.render(

<TodoForm teks={judul} />,

document.getElementById("content")

);

Kita kemudian dapat mengakses nilai atribut ini dengan menggunakan


properti props pada objek yang dikirimkan keReact.createClass:
1
2
3
4
5
6
7
8

var TodoForm = React.createClass({


render: function () {
return (
<div className="todoForm">
<label htmlFor="todoInput">
{ this.props.teks }
</label>

<input type="text" id="todoInput" className="inputForm" />

</div>

10

);

11
12

}
});

Penggabungan beberapa komponen dalam React juga dilakukan dengan cukup


natural: hanya dengan menggunakan komponen-komponen yang telah kita
kembangkan layaknya sebuah tag HTML biasa. Pertama, mari kita buat komponen
baru untuk menampung semua data yang ada:
1
2
3
4

// data boleh berasal dari mana saja.


// asumsikan data sudah didapatkan.
var data = [
{content: "Beli Telur", tag: "belanja"},

{content: "Tugas Javascript", tag: "kuliah"},

{content: "This War of Mine", tag: "game"},

7
8
9
10
11
12
13

{content: "Doraemon", tag: "film"}


];

var List = React.createClass({


render: function () {
var listData = this.props.data.map(function (data) {
return (

14

<li>{data.content}, tag: {data.tag}</li>

15
16
17
18
19

);
});

return (
<ul className="list">

20

{listData}

21
22
23

</ul>
);

24

}
});

Elemen List yang kita kembangkan melakukan sesuatu yang menarik. Data yang
kita peroleh, berupa array dari objek, dipetakan menjadi array dari
elemen <li> JSX pada variabel listData. listData kemudian langsung kita
masukkan sebagai nilai literal di dalam <ul> yang dikembalikan, dan React
secara otomatis mengetahui apa yang harus dilakukan dengan array <li>!
Penggabungan List dan TodoForm dapat dilakukan dengan cukup alami:
1

var TodoList = React.createClass({

render: function () {

return (

<div className="TodoList">

<h1>Todo List</h1>

<TodoForm teks="Tambahkan Data : " />

<List data={this.props.data} />

</div>

);

10
11

}
});

12
13

React.render(

14

<TodoList data={data} />,

15

document.getElementById("content")

16

);

Perlu dicatat bahwa sebuah elemen yang dikembalikan oleh komponen React hanya
boleh terdiri dari satu elemen terluar. Hal ini berarti kode berikut:
1
2
3

var TodoList = React.createClass({


render: function () {

return (

<TodoForm teks="Tambahkan Data : " />

<List data={this.props.data} />

);

7
8

}
});

Tidak akan dapat berjalan dan memberikan kesalahan kompilasi. Untuk


menanggulangi hal ini pastikan untuk selalu membuat elemen pembungkus agar
hanya terdapat satu elemen terluar saja.
Mengambil Data Dari Server
Aplikasi web umumnya tentunya tidak menyimpan data di dalam array secara
lokal seperti contoh pada bagian sebelumnya. Yang biasanya terjadi adalah
pengambilan data dilakukan melalui sisi server. Untuk dapat melakukan hal
ini, kita akan melakukan dua hal. Pertama, pada todo.html kita akan
menambahkan jQuery:
1

<head>

<script src="build/jquery-2.1.1.min.js"></script>

<script src="build/react.js"></script>

<script src="build/JSXTransformer.js"></script>

</head>

Kita kemudian perlu membuat file data.json yang isinya tidak berbeda jauh
dengan variabel data yang kita buat sebelumnya:
1
2
3

[
{
"content": "Beli Telur",

4
5
6
7

"tag": "belanja"
},
{

"content": "Tugas Javascript",

"tag": "kuliah"

},

10

11

"content": "This War of Mine",

12

"tag": "game"

13

},

14

15

"content": "Doraemon",

16

"tag": "film"

17
18

}
]

Data pada data.json ini nantinya yang akan kita ambil dari sever.
Selanjutnya, kita tidak akan langsung menggunakan variabel data lagi untuk
mengisikan list. Kita akan mengirimkan URL data yang diperlukan saja.
1
2

var TodoList = React.createClass({


render: function () {

return (

<div className="TodoList">

<h1>Todo List</h1>

<TodoForm teks="Tambahkan Data : " />

<List url={this.props.url} />

</div>

);

10
11
12
13
14
15
16

}
});

React.render(
<TodoList url="./data.json" />,
document.getElementById("content")

);

Selanjutnya, kita perlu memperbaharui komponen List seperti berikut:


1
2
3

var List = React.createClass({


loadData: function () {
$.ajax({

url: this.props.url,

dataType: 'json',

success: function (data) {

this.setState({data: data})

}.bind(this),

error: function (xhr, status, err) {

10

console.log(xhr);

11

console.log(status);

12

console.log(err);

13

}.bind(this)

14
15
16
17
18
19
20
21
22
23
24

});
},
getInitialState: function () {
return { data: [] }
},
componentDidMount: function () {
this.loadData();
},
render: function () {
var listData = this.state.data.map(function (data) {
return (

25

<li>{data.content}, tag: {data.tag}</li>

26
27
28
29

);
});

return (

30

<ul className="list">

31

{listData}

32

</ul>

33

);

34
35

}
});

Terdapat beberapa method tambahan yang digunakan di sini.


Pertama, method loadData merupakan method buatan kita untuk mengambil data
dari server. loadData cukup sederhana, hanya melakukan pengambilan data
melalui fungsi$.ajax dari jQuery.
Pada aplikasi kita sejauh ini, setiap komponen yang kita kembangkan
menampilkan dirinya berdasarkan props yang kita kirimkan. Nilai props tidak
dapat berubah isinya. props dikirimkan oleh komponen teratas, dan dimiliki
oleh komponen tersebut. Karena hanya dikirimkan kepada objek, maka
nilai props tidak dapat diubah. Jika ingin mengubah data untuk mendapatkan
interaktifitas aplikasi, kita perlu menggunakan mekanisme khusus yang
disediakan React.this.state merupakan komponen privat yang dapat diganti
isinya dengan memanggil this.setState. Ketika nilaistate diperbaharui,
komponen React akan secara otomatis diperbaharui juga
(fungsi render dipanggil kembali).
Fungsi render sendiri dituliskan sebagai fungsi deklaratif
milik this.props dan this.state. Hal ini menjadikan komponen antarmuka selalu
konsisten dengan interaksi pengguna.
Data dari this.state sendiri kita inisialisasi melalui
fungsi getInitialState. Nilai yang dikembalikan getInitialStateakan menjadi
nilai awal dari this.state. Fungsi ini dijamin oleh React hanya akan berjalan
sekali selama masa hidup komponen.
Method componentDidMount dipanggil secara otomatis oleh React ketika komponen
digambarkan. Kita memanggilthis.setState di sini agar data diperbaharui
secara dinamis. Array data yang lama kita gantikan dengan data yang diambil
dari server, dan komponen (UI) akan secara otomatis memperbaharui dirinya

sendiri. Karena ini, kita dapat dengan mudah mengimplementasikan live update,
dengan teknik polling:
1

componentDidMount: function () {

this.loadData();

setInterval(this.loadData, 2000);

},

Tentu saja kita dapat mengimplementasikan metode live update yang lebih baik,
misalnya dengan menggunakan WebSocket di bagian ini.

Anda mungkin juga menyukai