Me-manage History Browser Dengan jQuery dan History.js Pada Halaman Web yang Menggunakan AJAX

Standard

Salah satu hal yang harus di-handle ketika menggunakan teknik AJAX adalah history di browser. Saat menggunakan AJAX untuk mengambil data di suatu alamat, proses pengambilannya dilaksanakan secara asynchronous tanpa memindahkan halaman, sehingga browser tidak mencatat history dari alamat yang pernah kita kunjungi sebelumnya. Hal ini tentu saja akan merepotkan jika nanti kita ingin mengakses suatu alamat tertentu yang telah diakses sebelumnya dan ternyata datanya diambil dengan menggunakan AJAX.

Pada kasus yang saya alami kali ini, ceritanya saya ingin membuat sebuah aplikasi web yang kira-kira kerangka tampilannya seperti ini:

Skema website yang akan dibangun.

Skema website yang akan dibangun.

Masing-masing link di menu navigasinya akan mengarah ke suatu alamat di server yang nantinya dengan menggunakan AJAX, isinya akan diletakkan di sebuah <div> yang ada di tengah halaman tersebut.

Sederhana bukan? Tapi ternyata implementasinya tidak terlalu sederhana juga. -_-

Saya mengembangkan halaman ini dengan menggunakan MVC framework untungnya, jadi tidak harus repot saat harus menangani request yang masuk ke server.

Untuk mengimplementasikan halaman ini, ceritanya saya memberikan sebuah class bernama ajax-link untuk setiap link yang akan mengakses halaman lain dengan teknik AJAX. Masing-masing link ini diberi 1 buah atribut khusus tambahan yang bernama target-element yang berisi lokasi tempat si data yang telah diambil akan diletakkan.

Ini contoh link-nya:

<a class="ajax-link" href="/site/about" target-element="div#content">Click me!</a>

Link di atas akan mengakses alamat /site/about dengan menggunakan AJAX, dan meletakkan hasilnya ke sebuah div dengan id yang bernama content.

Untuk menangani link ini, saya membuat sebuah fungsi yang akan mengambil konten dari URL yang diberikan (sourceUrl) dan kemudian menampilkannya ke tempat yang ditargetkan (targetElement):

function loadContent(sourceUrl, targetElement, callback) {

    // Fade out existing content in targetElement.
    $(targetElement).fadeOut("slow", function() {

        // Fade in loading image while downloading data.
        $("#loading").fadeIn("slow", function() {

            // Load data from the server.
            $(targetElement).load(sourceUrl, function(response, status, xhr) {
                if (status == "success") {
                    $("#loading").fadeOut("slow", function () {
                        $(targetElement).fadeIn("slow");

                        // Run the callback function if any.
                        typeof callback === 'function' && callback();
                    });
                }
                else if (status == "error") {
                    $("#loading").fadeOut("slow", function () {
                        $(targetElement).text("Error!");
                        $(targetElement).fadeIn("slow");
                    });
                }
                else {
                    // TODO: Handle unknown result.
                }
            });
        });
    });
}

Sisanya, tinggal menambahkan listener di setiap link yang memiliki class ajax-link dengan menggunakan jQuery:

$(".ajax-link").click(function() {

    // Prevent browser to do page redirection when the link is clicked.
    event.preventDefault();

    // Get the data source URL.
    var sourceUrl = $(this).attr("href");

    // Get the target element for data.
    var targetElement = $(this).attr("target-element");

    // Load content from server (the implementation is separated)
    loadContent(sourceUrl, targetElement, function() {
        // Callback function is here.
    });
});

Kode di atas sudah bisa dipakai, tapi masalahnya, setiap berpindah halaman, halaman yang telah diakses sebelumnya tidak akan tersimpan.

Setelah membaca-baca dan melakukan coba-coba, ternyata saat menggunakan AJAX, operasi terkait dengan history (termasuk tombol back dan forward harus di-handle sendiri.

Setiap browser punya operasi untuk menyimpan history saat membuka halaman web. Di setiap browser, operasi untuk menangani history bisa didapatkan dengan mengakses objek window.history. Panduan untuk memanipulasi history bisa dilihat di sini.

Pertamanya saya tidak terlalu khawatir soal history ini, tetapi kekhawatiran itu tiba-tiba timbul saat saya melihat tabel ini di halaman Mozilla Developer Network:

Setelah membaca-baca sumber lain lagi sembari googling, ternyata ada Javascript library yang bisa digunakan untuk menangani hal ini, namanya History.js.

Kekhawatiran soal isu kompatibilitas setidaknya bisa diminimalisir dengan menggunakan library ini. Dengan menggunakan History.js, fungsi untuk melakukan operasi manipulasi history telah tertanam di dalamnya dan tinggal digunakan saja.

$(document).ready(function() {

    // Initialize History.js
    var historyJs = window.History; // 'History', not 'history'.

    // Listen for click in every link that has .ajax-link class.
    $(".ajax-link").click(function() {

        // Prevent browser to do page redirection when the link is clicked.
        event.preventDefault();

        // Get the data source URL.
        var sourceUrl = $(this).attr("href");

        // Get the target element for data.
        var targetElement = $(this).attr("target-element");

        // Load content from server (the implementation is separated)
        loadContent(sourceUrl, targetElement, function() {

            // Save history.
            historyJs.pushState(null, '', sourceUrl);
        });
    });
});

Fungsi pushState yang dipanggil di bagian callback akan menyimpan URL yang sedang dibuka ke dalam history. Yap, dengan ini, fungsi history sudah berfungsi.

Ups, tapi belum selesai. Belum cukup perjuangannya. :D

Kalau kode di atas dicoba, walaupun tombol back ditekan, halaman yang ditampilkan tidak akan berubah. Itu karena fungsi untuk meng-handle tombol back di browser ketika diklik oleh pengguna belum dibuat.

Untuk menangani tombol back, sebenarnya browser memiliki event khusus bernama onpopstate yang bisa di-listen. Tetapi karena halaman ini menggunakan History.js, sebisa mungkin interaksi langsung ke implementasi browser sebaiknya dijembatani oleh library saja agar implementasinya lebih konsisten.

Jadi, untuk menangani tombol back dan forward, fungsi pushState dapat dimanfaatkan dengan lebih maksimal. Fungsi pushState membutuhkan 3 parameter: sebuah state object, judul halaman, dan URL.

Soal state object, dikutip dari Mozilla Developer Network:

  • state object — The state object is a JavaScript object which is associated with the new history entry created by pushState(). Whenever the user navigates to the new state, a popstate event is fired, and the state property of the event contains a copy of the history entry’s state object.

    The state object can be anything that can be serialized. Because Firefox saves state objects to the user’s disk so they can be restored after the user restarts the browser, we impose a size limit of 640k characters on the serialized representation of a state object. If you pass a state object whose serialized representation is larger than this to pushState(), the method will throw an exception. If you need more space than this, you’re encouraged to use sessionStorage and/or localStorage.

Untuk state object, saya menyimpan sebuah angka state counter dan target elemen dari si link yang diklik sebelumnya. Angka state counter tersebut disimpan dalam sebuah variabel global yang bernilai awal 0. Setiap kali link diklik, variabel state counter ini akan bertambah, kemudian dengan menggunakan History.js, dengan memasang fungsi bind, jika terdeteksi bahwa ada state yang berubah, secara otomatis akan dilakukan pengecekan apakah nomor state counter yang berubah telah ada sebelumnya. Jika ternyata angkanya telah ada, tentu saja artinya si pengguna telah mengklik tombol back di browser.

Implementasinya kira-kira seperti berikut:

var GLOBAL_STATE_COUNTER = 0;

$(document).ready(function() {

    // Initialize History.js
    var historyJs = window.History; // 'History', not 'history'.

    // Bind the adapter to listen 'statechange' event.
    historyJs.Adapter.bind(window, "statechange", function() {

        // Get state object.
        var state = historyJs.getState();

        // Check whether back button is pressed.
        if (state.counter < GLOBAL_STATE_COUNTER) {

            // Reload the content.
            loadContent(state.url, state.targetElement, function() {
                // Callback function is here.
            });
        }
    });

    // Listen for click in every link that has '.ajax-link' class.
    $(".ajax-link").click(function() {

        // Prevent browser to do page redirection when the link is clicked.
        event.preventDefault();

        // Get the data source URL.
        var sourceUrl = $(this).attr("href");

        // Get the target element for data.
        var targetElement = $(this).attr("target-element");

        // Load content from server (the implementation is separated)
        loadContent(sourceUrl, targetElement, function() {

            // Save history, and save the state object.
            historyJs.pushState(
               {
                   counter: GLOBAL_STATE_COUNTER,
                   targetElement: targetElement
               },
               '', // It should be filled with document title, but just ignore it for now.
               sourceUrl
            );

            // Increment global state counter.
            GLOBAL_STATE_COUNTER++;
        });
    });
});

Nah, dengan ini, tombol back dan forward-nya bisa berfungsi. ;)

Oh iya, ada beberapa catatan terkait dengan kasus ini yang rasanya penting untuk diketahui saat membangun aplikasi web yang menggunakan AJAX, untuk implementasi di sisi server-nya. Berikut beberapa hal yang saya rasa perlu diperhatikan:

Judul Halaman

Kode di atas tidak meng-handle judul yang ditampilkan di halaman web yang dibuat. Hal ini tentunya nanti harus diatur agar tampilan halamannya lebih informatif. Untuk mengubah judul halaman, pembaca tinggal mengubah atribut document.title lewat Javascript.

Menangani Request AJAX dan non-AJAX

Jika aplikasi web yang dikembangkan semakin kompleks, masing-masing alamat di server bisa jadi akan mengembalikan response yang content type-nya berbeda-beda. Data yang dikembalikan mungkin bisa saja berupa JSON, gambar, atau mungkin pecahan dari suatu halaman jika request yang masuk ternyata adalah sebuah AJAX request. Namun jika request yang masuk ternyata request biasa, sang server harus mengembalikan response yang berbeda.

AJAX request bisa diidentifikasi dari header-nya. Biasanya ada entry di header yang bernama HTTP_X_REQUESTED_WITH yang menjadi penanda bahwa request dikirimkan dari sebuah XmlHttpRequest. Javascript library pada umumnya sudah menyertakan header ini, dan sang server tinggal membacanya saja.

Untuk mengenali apakah suatu request berasal dari AJAX atau tidak, beberapa framework tertentu sudah menyediakan fungsi built-in. Di ASP.NET misalnya, sang programmer tinggal menggunakan statement Request.IsAjaxRequest() di view-nya. Di framework MVC lain seperti Laravel ada fungsi Request::ajax(), dan di CakePHP ada fungsi $this->request->is('ajax'). Implementasinya berbeda-beda, tergantung bahasa dan framework yang digunakan.

Fiuh, kira-kira demikian cerita saya tentang menangani history dengan Javascript ini. Rasanya AJAX nanti (atau mungkin sudah?) akan jadi trend untuk aplikasi web modern yang dinamis dan responsif. Karena itu, hal-hal kecil seperti ini saya rasa cukup penting untuk diketahui. Semoga bermanfaat. :D

Mempelajari Pemrograman Web

Standard

Sudah 3 tahun terakhir ini semenjak saya benar-benar ‘menjeburkan’ diri di bidang teknologi informasi dan mengawalinya dengan menjalani kuliah di program studi teknik informatika, sebagian besar aktifitas saya dihabiskan di web browser: emailblogging, menggunakan cloud computing dan banyak hal lainnya. Aplikasi desktop yang ada di komputer saya jadi banyak yang menganggur.

Kalau saat ini, aplikasi dekstop yang cukup sering saya gunakan paling hanya Microsoft Office, 7zip (untuk menangani compressed archive), beberapa IDE (NetBeans, Visual Studio), SourceTree (untuk version control system), beberapa teks editor canggih (Sublime, Notepad++) dan MuseScore (untuk menulis partitur lagu). Ada juga beberapa aplikasi yang terkait dengan sistem seperti Windows Explorer atau command prompt. Aplikasi yang selain disebutkan di atas jadi jarang sekali dipakai.

Jangan-jangan nanti semua komputer akan jadi thin client semua ya? Cukup hanya dengan web browser dan semua hal bisa dilakukan. Bisa jadi juga kan ya?

Bayangkan, kalau mau mencari video, sekarang sepertinya orang-orang lebih suka streaming di YouTube. Untuk memutar lagu, web browser sekarang juga bisa. Aplikasi web seperti SoundCloud misalnya, penuh dengan rekaman data audio yang menarik. Untuk pekerjaan kantor tidak perlu ditanya lagi. Ada Google Drive dan SkyDrive. Semuanya bisa dijalankan di web browser.

Jadi, kalau bicara masalah peluang di masa depan, saya rasa belajar web programming merupakan hal yang sangat tepat. :D

Ada yang tertarik untuk mempelajari web programming? Tulisan saya kali ini membahas tentang langkah-langkah yang bisa diikuti jika pembaca ingin mempelajari pemrograman web. ;)

Mendesain Layout Halaman Web

Pertama kali saat mempelajari hal-hal yang berkaitan dengan web, hal pertama yang harus dipahami adalah tentang Hypertext Markup Language (HTML) dan Cascading Style Sheet (CSS). HTML dan CSS merupakan gerbang pertama yang harus dipelajari untuk membuat halaman web.

Pembaca sekarang sedang melakukan browsing dengan Google Chrome atau Firefox? Coba sekarang tekan Ctrl + U.

Nah, sudah kembali ke halaman ini? :P Kode-kode panjang yang pembaca lihat setelah menekan shortcut tersebut merupakan elemen penyusun dari website yang sedang pembaca kunjungi ini.

Setiap halaman yang pembaca lihat di web browser tersusun dari struktur kode-kode HTML tersebut. Di dalamnya, bagian-bagian dari halaman tersebut diberi spesifikasi tampilan (style) dengan menggunakan CSS.

Ada banyak resources yang dapat digunakan oleh pembaca untuk mempelajari HTML. Coba saja kunjungi w3schools misalnya.

Tampilan halaman depan w3schools.

Sebenarnya, di dunia nyata, proses mendesain halaman web tidak langsung ke kode HTML dan CSS. Sebelum memutuskan untuk mengembangkan suatu halaman web, terlebih dahulu si pengembang harus memikirkan kira-kira bentuk website-nya akan seperti apa. Bentuk website tersebut pada awalnya digambar sketsanya atau biasa disebut juga wireframing. Setelah terpetakan, barulah desainer web bisa mendesain halamannya. Itupun tidak langsung menggunakan HTML dan CSS. Pada awalnya desain web tersebut hanya digambar biasa dengan software seperti Adobe Photoshop atau Illustrator.

Setelah desainnya benar-benar fix, barulah kemudian tampilan website-nya dibangun dengan HTML dan CSS.

Jika pembaca merasa sudah cukup mengerti tentang HTML dan CSS, cobalah pelajari bagaimana mengubah gambar desain menjadi halaman web yang sebenarnya. Ada banyak tutorial yang bisa pembaca cari. Biasanya saya melihat tutorial-tutorial tersebut di website-website ini: 1stwebdesigner, Tuts+, CSS-Tricks. Masih banyak website lain yang juga bagus. Googling saja. ;)

HTML 5, CSS 3, dan Lainnya

Jika pembaca sudah mulai menguasai dasar-dasar HTML, coba pelajari tentang HTML 5 dan CSS 3. Dengan 2 teknologi ini, pembaca bisa membuat sesuatu yang lebih powerful dibanding dengan HTML dan CSS biasa.  Bahkan dengan HTML 5, pembaca bisa membuat aplikasi seperti game. Selain itu jika pembaca juga mau mempelajari JavaScript, hanya dengan HTML, CSS dan JavaScript, pembaca bisa membuat aplikasi mobile. Coba lihat: PhoneGap, Steroids, dan Ionic Framework.

Mulai Mendalami Web Programming

Setelah mempelajari HTML dan CSS, langkah selanjutnya adalah mempelajari bahasa pemrograman web yang berjalan di sisi server. Ada buanyak (saking banyaknya) bahasa pemrograman yang bisa pembaca pelajari. Di antaranya ada PHP, ASP dan ASP.NET, Java, Ruby, Python dan lainnya. Pilihlah 1 bahasa dan fokuslah ke bahasa pemrograman tersebut sampai benar-benar menguasainya.

Kalau saran pribadi dari saya, pelajarilah PHP terlebih dahulu. Di antara bahasa-bahasa tersebut, PHP merupakan yang paling banyak penggunanya untuk  saat ini. Selain itu, nanti jika pembaca ingin mempunyai website sendiri misalnya, hosting di Indonesia rata-rata sudah mendukung PHP, sehingga akan lebih mudah untuk pembaca saat ingin mempraktikkan langsung hasil belajarnya.

Nanti, jika sudah menguasai PHP, berpindah ke bahasa lain tidak akan terlalu sulit. Saya pribadi bahkan tidak menganjurkan pembaca untuk langsung mempelajari Ruby, Python, dan ASP, karena bahasa-bahasa tersebut sangat strict dalam kerangka Model-View-Controller (akan saya bahas di bawah). Dengan PHP, pembaca bisa mempelajari konsep dasarnya dengan lebih mudah.

Bitnami WAMP Stack, salah satu tools yang dapat pembaca gunakan untuk mempelajari PHP web programming.

Bitnami WAMP Stack, salah satu tools yang dapat pembaca gunakan untuk mempelajari PHP web programming.

Oh iya, saat mempelajari web programming, pastikan pembaca mempelajari konsep form dengan benar. Di jagad web, yang membuat dunia di internet bisa seinteraktif seperti saat ini adalah karena adanya si tag <form> tersebut. Saat mempelajari form, pembaca pasti akan bertemu dengan terminologi seperti GET dan POST. Pelajari hal tersebut, karena disitulah nanti inti dari web programming. Jika pembaca sudah berurusan dengan form, biasanya pembaca juga akan berurusan dengan database. Pelajari pula syntax dasar dari Structured Query Language (SQL) seperti INSERT, SELECT, UPDATE dan DELETE. Pembaca akan akrab dengan 4 keywords tersebut.

Oh, satu lagi, bertahanlah dalam mempelajari programming-nya sendiri. Sebelum mempelajari hal seperti GET dan POST tadi, pembaca pasti akan berurusan terlebih dahulu dengan dasar-dasar pemrograman seperti branching (percabangan) dan looping (perulangan). Setelah itu, pembaca juga pasti akan bertemu tentang konsep function dan bahkan Object Oriented Programming. Di sinilah kesungguhan pembaca dalam mempelajari dunia komputer akan diuji. Mereka yang berhasil di tahap ini biasanya akan sukses. ;)

Nah, tentang sumber untuk belajarnya, kalau saran pribadi dari saya, cobalah cari buku atau e-book soal web programming. Sumbernya bisa banyak sekali. Untuk yang mau belajar PHP, di Google ketikkan saja php programming filetype:pdf dan pembaca akan kebanjiran dokumen PDF yang membahas tentang web programming menggunakan PHP.

Tampilan halaman it-ebooks.info, website penyedia e-book komputer gratis.

Tampilan halaman it-ebooks.info, website penyedia e-book komputer gratis.

Oh iya, satu lagi, satu lagi. :P Bertahanlah dengan banyaknya konten yang ber-Bahasa Inggris, karena Bahasa Inggris adalah lingua franca-nya para ahli komputer. Just keep learning, dan pastikan Google Translate ada di salah satu tab pembaca saat browsing. :D

Untuk memastikan bahwa pembaca benar-benar mengerti tentang web programming, cobalah membuat aplikasi web sederhana dari scratch, yakni benar-benar dari 0. Pembaca bisa membuat aplikasi blog sederhana, atau buku tamu sederhana, atau mungkin galeri foto sederhana, atau bahkan jejaring sosial sederhana? Apapun itu, cobalah membuatnya sampai benar-benar jadi. Pembaca akan lebih banyak mendapatkan ilmunya nanti, dibanding hanya sekedar membaca buku. Pastikan pembaca benar-benar mempraktikkannya ya. :)

Melompat ke Advanced Topics

Ketika pembaca merasa sudah benar-benar mengerti tentang bagaimana penggunaan HTML dan CSS, serta bagaimana mengintegrasikannya agar dapat mengirim dan mengambil data secara dinamis ke database dengan menggunakan salah satu bahasa pemrograman web, cobalah untuk melanjutkannya dengan mempelajari hal-hal berikut:

MVC Framework

Ada kalanya nanti ketika pembaca membuat 1 aplikasi web yang cukup kompleks, dan kode pembaca sudah demikian banyak, sehingga rasanya seperti berantakan sekali. Jika pembaca telah merasa demikian, ada baiknya pembaca mempelajari penggunaan MVC framework.

MVC adalah singkatan dari model-view-controllersebuah software-pattern yang memungkinkan pembaca untuk menyusun aplikasi dengan 3 objek utama yaitu model (sebagai entitas utama yang digunakan untuk mengakses data), view (sebagai tampilan yang dilihat pengguna) dan controller (sebagai pengendali utama aplikasi).

Skemanya kurang lebih bisa dilihat di gambar di bawah ini:

Tipikal kolaborasi dari komponen MVC.

Bahasa pemrograman web seperti Ruby dan ASP.NET telah menganut pattern ini di dalam fungsi utamanya. Mempelajari Ruby dan ASP.NET akan membuat pembaca merasakan langsung bentuk pattern ini.

Di PHP, ada beberapa MVC framework yang dapat pembaca gunakan, di antaranya: Yii, CakePHP, Laravel, Zend, Symfony.

Dengan menggunakan framework, aplikasi yang pembaca buat akan lebih terstruktur dan lebih mudah untuk dipelihara. Jika pembaca terjun di dunia web programming yang sebenarnya, ketika suatu aplikasi web dikembangkan oleh banyak programmer, barulah nanti akan terasa bahwa menggunakan framework yang telah ada atau setidaknya menggunakan pattern MVC akan mempermudah pengembang untuk terus memperbarui aplikasinya.

CakePHP, salah satu PHP MVC framework.

CakePHP, salah satu PHP MVC framework.

Javascript dan AJAX

Pembaca pernah melihat website yang di halaman depannya ada slideshow gambar, atau ada efek-efek transisi atau mungkin efek bergerak yang menarik? Nah, disitulah peran Javascript.

Javascript adalah sebuah bahasa pemrograman dynamic yang berjalan di sisi client dan merupakan bagian dari web browser yang pembaca pakai. Dengan menggunakan Javascript, pembaca bisa memodifikasi elemen-elemen  (Document Object Model atau DOM) halaman web secara langsung dan berinteraksi dengan pengguna dengan konten yang interaktif. Mempelajari Javascript akan membantu pembaca untuk memperkaya user experience dari halaman web yang pembaca kembangkan.

Jika pembaca ingin mempelajari dasar-dasar dari Javascript, pembaca bisa mempelajarinya secara interaktif di Codecademy. Di sana nanti pembaca akan mendapatkan instruksi-instruksi yang bisa pembaca ikuti untuk mendalami Javascript.

Jika pembaca sudah mulai menguasainya, cobalah mencoba-coba kode Javascript yang pembaca pelajari dengan meletakkannya langsung di halaman web sebagai latihan.

Nah, setelah merasa sudah mulai menguasai Javascript, pembaca bisa mencoba Javascript library seperti jQuery misalnya. Dengan menggunakan library, pembaca bisa melakukan banyak aksi-aksi ataupun menambahkan efek-efek tertentu tanpa harus membuatnya dari awal.

Halaman depan website jQuery.

Halaman depan website jQuery.

Di Javascript, ada sebuah teknik menarik yang disebut AJAX. AJAX merupakan singkatan dari Asynchronous Javascript and XML, yaitu sebuah teknik untuk menciptakan aplikasi web yang responsif. Dengan menggunakan AJAX, pengguna halaman web tidak perlu berpindah dan me-load halaman dari awal, karena segala sesuatunya telah dilakukan dibelakang pengguna secara asynchronous tanpa pengguna sadari.

Dengan menggunakan AJAX, website akan terasa lebih responsif dan interaktif di mata pengguna. SoundCloud, Facebook dan Twitter adalah contoh aplikasi yang menggunakan AJAX. Saat mengetikkan komentar di salah satu posting di Facebook misalnya, pembaca cukup mengetikkan komentar dan menekan enter, dan tahu-tahu komentar pembaca sudah ditampilkan di sana bukan? Sebenarnya, tanpa disadari, saat penggunanya menekan enter setelah mengetik komentar, sang Facebook di belakang layar melakukan pengiriman data ke server dan langsung menampilkannya tanpa harus berpindah halaman.  Seperti itulah contoh pemanfaatan AJAX. Kalau pembaca sudah menguasai jQuery, melakukan AJAX tidak akan menjadi pekerjaan sulit.

MV* Framework Dengan Javascript

Beberapa aplikasi web, ada yang sebagian besar kodenya merupakan kode Javascript. Biasanya, aplikasi yang didesain hanya berkutat di 1 halaman (disebut juga single page application atau disingkat SPA) akan mengalaminya. Saat sebagian besar aksi perpindahan halaman harus ditentukan dengan menggunakan Javascript, tentu kode Javascript yang ada akan menjadi banyak dan biasanya tidak terstruktur, sama kasusnya seperti kasus MVC pada bahasa pemrograman web seperti PHP yang kita bahas sebelumnya di atas.

Untuk mengatasi hal ini, konsep MVC pun juga dianut dalam pemrograman Javascript, hanya saja yang dipakai adalah konsep turunannya, dan ada banyak sebutan dari turunan pattern MVC ini. Ada yang menyebutnya MVVM (model-view-view model), ada juga yang menyebutnya MVP (model-view-presentation). Karena itu, biasanya turunan-turunan dari MVC ini disingkat menjadi MV*.

Ada banyak MV* framework ini dan tentunya semuanya diprogram dengan Javascript. Di antaranya ada: AngularJS, Backbone.js, Knockout.js, dan Ember.jsFramework-framework ini sudah banyak digunakan di berbagai aplikasi-aplikasi yang besar. Framework Backbone.js saja sudah dipakai di aplikasi seperti WordPress, Four Square dan SoundCloud (coba lihat di sini).

Halaman depan website AngularJS.

Halaman depan website AngularJS.

Demikianlah kira-kira langkah-langkah yang dapat diikuti untuk mempelajari pemrograman web. Tulisan ini kurang lebih bersumber dari pengalaman pribadi saya juga, hehe. Pada akhirnya, siapapun orangnya dan bagaimanapun caranya saat mempelajari web programming pasti akan berhadapan dengan beberapa atau mungkin semua hal yang saya bahas di tulisan ini. Jika ada pembaca yang ingin menambahkan, memberi saran atau mengkritik terkait tulisan ini, silakan disampaikan lewat komentar, dan akan saya terima dengan tangan terbuka.

Mempelajari sesuatu merupakan tantangan yang besar, dan ketika berhasil pasti akan sangat menyenangkan rasanya. Mudah-mudahan pembaca yang ingin mempelajari web programming bisa dipermudah dengan adanya tulisan ini. Semoga bermanfaat. :D

Menangani Penyimpanan File yang di-Upload ke Server

Standard

Dalam pembuatan aplikasi web, terkadang dibutuhkan suatu skenario untuk meng-upload satu atau beberapa file ke server. Untuk mengganti gambar profil di aplikasi jejaring sosial misalnya, pengguna pasti akan berurusan dengan skenario ini.

Proses meng-upload file pada aplikasi web ditangani oleh masing-masing infrastruktur dari aplikasi web yang digunakan, namun hampir seluruh urutan prosesnya sama: user akan memilih file, aplikasi web akan menyimpan file yang di-upload oleh user ke suatu path dengan nama file tertentu, kemudian jika dibutuhkan informasi file tersebut akan disimpan di database. Hal yang sering menjadi masalah adalah tentang metode menyimpan file-file tersebut.

Tulisan saya kali ini membahas beberapa cara yang bisa dilakukan untuk menangani penyimpanan file yang di-upload ke server lewat aplikasi web.

Cara 1: Simpan File Apa Adanya

Menyimpan file apa adanya adalah cara yang paling mudah dan juga paling tidak direkomendasikan. -_- Simpan file yang telah di-upload ke suatu direktori tertentu dengan nama yang sama persis saat di-upload.

Misalkan, ada seseorang bernama Budi yang menyimpan foto profil di komputernya dengan nama budi.jpg, maka aplikasi akan meng-upload dan menyimpannya juga dengan nama budi.jpg di suatu direktori di server.

Masalahnya akan muncul, jika ternyata ada Budi lain (sebut saja Budi 2) yang juga menyimpan foto profil dengan nama budi.jpgFile yang sama tentu saja akan tertimpa.

Jika hal seperti ini terjadi, tentu saja gambar foto profil Budi 1 dan Budi 2 akan selalu sama. Sedangkan di Indonesia mungkin ada ribuan orang dengan nama Budi (Budi 3, Budi 4, dan seterusnya). Dengan cara ini, gambar foto profil ribuan orang ini pasti akan selalu sama jika mereka meng-upload file dengan nama budi.jpg. Bayangkan saja, mereka akan saling berebut berganti gambar profil bukan? :P

Cara ini hanya untuk ilustrasi kemungkinan terburuk saja. Sebisa mungkin hindari cara sederhana ini, kecuali jika dapat dipastikan bahwa tiap user akan meng-upload file yang namanya pasti unik.

Cara 2: Simpan File di Dalam Database

Banyak Database Management System (DBMS) yang menyediakan tipe data binary untuk dapat menyimpan file langsung di simpan di dalam database. Tipe data binary large object (BLOB) di MySQL misalnya, dengan menggunakan tipe data ini, file yang di-upload oleh user dapat langsung di simpan di database dengan statement INSERT seperti biasa.

Namun sebaiknya sebelum menggunakan cara ini, pelajari tentang efek penyimpanan binary file  di dalam database terhadap performa dari DBMS yang digunakan, karena ada catatan tertentu terkait soal penggunaan cara ini.

Sebagai contoh, untuk pengguna DBMS MySQL, coba lihat halaman ini: http://stackoverflow.com/questions/5285857/when-is-using-mysql-blob-recommended

Cara 3: Simpan File di Direktori Tertentu Dengan Nama Khusus

Cara ketiga yang cukup mudah dan paling umum adalah menyimpan file tersebut dengan suatu nama khusus tertentu. Untuk cara ini, ada beberapa cara yang bisa dilakukan.

Contoh sederhana misalnya, aplikasi bisa saja menyimpan file yang telah di-upload tersebut dengan format nama tertentu. Sang programmer dapat membuat prosedur sedemikian sehingga setiap file yang di-upload akan disimpan dengan nama file-n.extension di mana n adalah urutan upload -nya. Jadi nanti di dalam direktori tempat file upload akan ada file-1.extension, file-2.extension dan seterusnya. Sedangkan informasi file yang aslinya bisa disimpan di dalam database.

Kelemahan metode ini adalah, sang programmer harus memikirkan sebuah fungsi untuk meng-generate format nama ini dengan benar tanpa kemungkinan adanya saling timpa.

Cara lain bisa juga dengan cara meng-generate sejenis random characters tertentu yang nantinya akan digunakan sebagai nama file. Dengan cara ini, sang programmer tinggal memikirkan 1 fungsi untuk menghasilkan karakter acak yang stabil untuk nama file yang akan disimpan.

Ngomong-ngomong soal karakter acak, ada beberapa cara yang bisa dilakukan. Di antaranya, sang programmer bisa menggunakan fungsi untuk meng-generate sebuah Universally Unique Identifier. Banyak bahasa pemrograman yang sudah memiliki built-in function untuk melakukan hal ini.

Ini contoh UUID: 88961191-5ad3-49b2-be97-c3f481820644. Spesifikasinya diatur dalam RFC 4122.

Banyak metode untuk menghasilkan karakter acak. Biasanya, dengan menggunakan parameter timestamp, keunikan karakter bisa dijamin. Membungkus string angka timestamp dengan fungsi md5 misalnya, itu sudah cukup menghasilkan karakter yang lumayan acak (lihat fungsi PHP di bawah sebagai contoh).

$randomCharacters = md5(strtotime("now"));

Untuk fungsi yang lebih reliabel, bisa juga menggunakan fungsi checksum untuk file. Fungsi checksum akan menghasilkan serentetan string yang unik dan tentunya dapat digunakan untuk nama file yang di-upload. Hasil checksum bisa dikatakan sebagai sidik jari dari suatu file sehingga terjamin keunikannya.

Algoritma checksum ada banyak pilihannya, dan sama seperti UUID tadi, fungsi built-in-nya juga sudah banyak di berbagai macam bahasa pemrograman. Tinggal dipakai saja.

Cara 4: Simpan File di Dalam Struktur Direktori Tertentu Dengan Nama Khusus

Cara nomor 3 di atas, boleh dipakai dengan catatan: jumlah file dalam direktori tempat penyimpanan file yang di-upload tidak terlalu banyak.

Saat mengakses suatu direktori dengan menggunakan perintah ls di Linux atau dir di Windows, terlebih dahulu, sistem harus melakukan pendataan terhadap isi direktori. Itulah mengapa saat menggunakan file explorer di semua sistem operasi, semakin banyak menyimpan file di dalam direktori maka akan semakin lama proses loading-nya.

Selain itu, setiap disk tempat penyimpanan data memiliki batas maksimum jumlah file yang dapat disimpan di dalam 1 direktori. Batas maksimum ini dipengaruhi oleh jenis file system yang digunakan (lihat tabel di bawah).

Sistem Operasi File System Jumlah File Maksimum di 1 Direktori
Windows FAT-32 268,173,300
NTFS 4,294,967,295 (232-1)
Linux ext3 Tergantung Kapasitas Disk
ext4 Tergantung Kapasitas Disk
Mac OS HFS 65,535
HFS+ 4,294,967,295 (232-1)

Diambil dari banyak sumber. Jika ada yang keliru mohon beri pemberitahuan pada penulis.

Jika aplikasi web yang dikembangkan akan menghasilkan banyak sekali file yang di-upload seperti aplikasi penyimpanan gambar, faktor ini harus sangat diperhatikan.

Untuk menangani hal ini, diantaranya, sang programmer dapat memisahkan file-file yang di-upload ke dalam klasifikasi-klasifikasi yang lebih spesifik dalam bentuk direktori. Misalnya, jika file yang di-upload diletakkan di direktori upload/, maka jika ada user yang bernama imamfile-nya bisa diletakkan di direktori upload/imam/.

Klasifikasi yang dilakukan dapat dipilih sesuai kebutuhan. Direktori klasifikasi bisa saja dibuat berdasarkan parameter tertentu. Sebagai contoh, aplikasi WordPress menyimpan file yang di-upload di direktori wp-content/uploads/, dan kemudian diklasifikasikan berdasarkan tahun, dan di dalamnya diklasifikasikan lagi berdasarkan bulan.

Ada banyak skenario lain yang mungkin lebih advanced dalam menangani penyimpanan file yang di-upload. Tulisan ini hanya membahas beberapa diantaranya. Demikianlah kira-kira sekelumit cara yang dapat dilakukan untuk menyimpan file yang di-upload oleh user ke dalam server. Semoga bermanfaat. :)

Mengatasi Isu Cross-Origin Resource Sharing (CORS)

Standard

Ceritanya kemarin saya sedang mengembangkan sebuah aplikasi web bersama teman-teman seangkatan saya. Saya kebagian in charge di tim back end. Saat itu saya sedang mengerjakan halaman “Tentang Kami” yang berisi profil-profil anggota tim pengembang.

Aplikasi ini dikembangkan dengan menggunakan WordPress. Kebetulan, profil user di WordPress ditangani oleh Globally Recognized Avatars (Gravatar) yang berarti tentunya, detail profil user yang ada seperti foto, facebook, twitter dan lainnya harus di-fetch dari situs Gravatar tersebut.

Syukurnya, Gravatar punya API yang dapat digunakan oleh publik. Jadi saya gunakan saja API tersebut untuk mengambil data.

about-us-page

Tampilan halaman “Tentang Kami” yang sedang dikerjakan.

Sebagai informasi, jumlah tim kami ada 11 orang, dengan kata lain saya harus melakukan perulangan 11 kali untuk mengambil data profil dari situs tersebut. Belum lagi untuk menampilkan profil kontributor yang nanti jumlahnya bisa banyak. Pengambilan data akan dilakukan dengan menggunakan PHP biasa. Jadi, implementasinya waktu itu seperti ini:

foreach ($users as $user) {
    $str = file_get_contents( 'http://www.gravatar.com/' . md5($user->user_email) . '.php' );
    $profile = unserialize( $str );
    if ( is_array( $profile ) && isset( $profile['entry'] ) )
        /* Action to display profile... */
}

Saat dicoba, ternyata proses loading-nya lama sekali. -_- Saya baru sadar, proses pengambilan data oleh PHP dilakukan secara berurut. Dengan kata lain, profil si user akan diambil satu-satu, dan prosesnya harus ditunggu sampai selesai baru bisa mengambil profil lainnya. Kalau misalnya ada masalah di koneksi pada saat melakukan fetching salah satu profile, tentu saja semuanya jadi ikut bermasalah. Belum lagi kalau misalnya nanti jumlah user yang ada menjadi lebih banyak seiring berjalannya waktu. Mau berapa lama loading-nya?

Karena itu, supaya aman, proses fetching informasi profile dari Gravatar harus dilakukan secara paralel. Masalahnya, PHP tidak mendukung multithread secara default. Jadi, bagaimana?

Tentu saja, JavaScript!

Dengan menggunakan jQuery, profilnya akan diambil secara asynchronous, jadi tampilan halaman akan muncul terlebih dahulu, baru kemudian profilnya akan di-append satu-satu ke halaman tersebut. Untungnya Gravatar menyediakan API yang dapat digunakan untuk mengambil data dalam format JSON. Kodenya jadi begini:

$(document).ready(function() {
    $.get('http://www.gravatar.com/' + userMd5Hash + '.json', function(data) {
        /* Operation to parse JSON (emitted). */
        $("#" + userMd5Hash + "-accounts").append(/* ... */);
    });
});

Saat dicoba dijalankan, tiba-tiba saya mendapatkan pesan error di jendela console. Tampilannya mirip seperti di bawah ini:

cors-error

Nah, ada apa lagi ini? -_-

Ternyata, operasi GET menggunakan AJAX dengan menggunakan jQuery yang saya lakukan terbentur oleh same origin policy. Ini ada apa lagi ya? o.O

Ternyata..

Jadi, saya mengerjakan halaman ini di localhost, sedangkan profil yang ingin saya ambil, ada di alamat domain gravatar.com yang tentunya berbeda lokasinya. Secara defaultweb browser tidak mengizinkan langsung operasi pengambilan data yang berbeda domain seperti ini untuk alasan keamanan.

Setelah googling beberapa saat, ternyata untuk mengatasinya, harus dilakukan penyetelan yang memungkinkan untuk melakukan Cross-Origin Resource Sharing (CORS). Untuk melakukannya, ada 2 pilihan yang dapat dilakukan:

Enable CORS di Server Tujuan

Untuk dapat melakukan CORS, pemilik server harus menambahkan header khusus di setiap response yang dikembalikan dari tiap request yang diberikan yaitu:

Access-Control-Allow-Origin: *

Jika header ini tidak didapatkan di response yang dikirimkan balik, maka browser akan memberikan pesan error seperti yang ada di atas tadi.

API di website lain seperti GitHub misalnya, sudah mengizinkan CORS secara default (lihat gambar di bawah, dan perhatikan kotak merah):

github-response-header

Sedangkan, Gravatar tidak mengizinkannya (lihat gambar di bawah):

gravatar-response-header

Karena saya tidak mungkin mengotak-atik API server milik Gravatar, ya apa boleh buat. -_- Tapi untungnya, di sisi client ada hal lain yang dapat dilakukan, yaitu:

Gunakan JSON with padding (JSONP)

Teknik yang dapat dilakukan untuk mengatasi ini adalah menggunakan JSONP. Implementasinya, cukup dengan mengganti rutin jQuery yang digunakan, dan mengubah URL request dengan menambahkan parameter callback.

$(document).ready(function() {
    $.getJSON('http://www.gravatar.com/' + userMd5Hash + '.json?callback=?', function(data) {
        /* Operation to parse JSON (emitted). */
        $("#" + userMd5Hash + "-accounts").append(/* ... */);
    });
});

Fiuh, dan akhirnya berhasil juga. :D

Syukur sekali saya mendapatkan masalah ini. Kebetulan, aplikasi yang sedang saya kerjakan dan skripsi saya saat ini banyak berhubungan dengan RESTful API, dan isu seperti CORS seperti ini pasti akan sangat membingungkan nanti untuk orang yang menggunakan API yang saya kembangkan. Jadi, untuk pengembang REST API, sepertinya isu CORS seperti ini patut untuk diperhatikan.

Demikian cerita ini, mudah-mudahan bermanfaat untuk pembaca ya. :)

Pentingnya Modularisasi Dalam Pemrograman

Standard

Don’t Repeat Yourself (DRY). Pernah mendengar slogan ini? DRY merupakan sebuah prinsip pengembangan software yang makna utamanya adalah mengurangi repetisi semaksimal mungkin.

Mungkin repetisi yang pembaca lakukan tidak akan terlalu terasa kalau pembaca menulis software yang pendek (seperti tugas algoritma dan pemrograman di kelas misalnya). Kalau urusannya dengan software siap pakai beneran, barulah semuanya terasa. -_-

Contoh singkat, pernah menulis program dengan Java? Untuk mengeksekusi 1 query ke database MySQL saja misalnya, paling tidak harus ada kode seperti di bawah ini bukan?

import java.sql.*;

import javax.swing.JOptionPane;

public class MySQLSampleProgram {

    public static final String  DB_NAME     = "dbname";
    public static final String  DB_HOST     = "localhost";
    public static final int     DB_PORT     = 3306;
    public static final String  DB_USER     = "dbuser";
    public static final String  DB_PASSWORD = "dbpassword";

    public static Connection conn = null;
    public static Statement stat = null;

    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://" + DB_HOST + ":" + String.valueOf(DB_PORT) + "/" + DB_NAME, DB_USER, DB_PASSWORD);
            stat = conn.createStatement();

            String query = "SELECT * FROM table;";

            ResultSet rs = stat.executeQuery(query);

            /* Continued... */
        } catch (Exception e) {
            JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
        }
    }
}

Baru setelah melakukan eksekusi terhadap query, hasil yang didapatkan dari database bisa dipakai. Bayangkan kalau setiap form yang pembaca buat harus mencantumkan kode konfigurasi database di atas (DB_NAME dan seterusnya) dan kode yang ada di blok try di atas (Class.forName dan seterusnya). Fiuh, capek bukan?

Untuk itulah pentingnya modularisasi. Dengan mengelompokkan operasi-operasi yang sering dilakukan ke dalam suatu modul (function, procedure atau class), maka jika dibutuhkan, operasi-operasi tersebut tinggal dipanggil dengan cara yang singkat. Di sinilah pemrograman berorientasi objek menjadi menyenangkan. ;)

Dengan menganggap masalah yang hendak diproses sebagai objek, maka proses pemecahan masalah dapat dilakukan langsung tanpa harus melaksanakan serentetan operasi yang berulang-ulang. Untuk menyelesaikan contoh kasus koneksi database di Java seperti di atas misalnya, saya melakukan modularisasi dengan cara menulis class yang dapat digunakan untuk menyederhanakan eksekusi query. Coba lihat di sini: https://gist.github.com/imamhidayat92/5418582

Kalau pembaca menggunakan class yang saya tulis tersebut, mengeksekusi query hanya akan sekedar memanggil perintah sederhana seperti berikut:

import helper.MySQLConnector;

import java.sql.*;

import javax.swing.JOptionPane;

public class MySQLSampleProgram {

    public static void main(String[] args) {
        try {
            String query = "SELECT * FROM table;";

            ResultSet rs = MySQLConnector.executeQuery(query);

            /* Continued... */
        } catch (Exception e) {
            JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
        }
    }
}

Hemat bukan jadinya? :P

Melakukan sesuatu yang repetitif itu akan sangat melelahkan. Tanpa modularisasi, proses meng-update kode juga akan menjadi rumit. Bayangkan, ada berapa kode serupa yang harus diperbaiki kalau ternyata ada prosedur yang salah?

Dengan modularisasi, tentu saja menulis program juga akan menjadi lebih efisien, dan kodenya akan lebih mudah dibaca.

Semoga bermanfaat. ;)