Elemen UI databinding dengan IndexedDB

Raymond Camden
Raymond Camden

Pengantar

tensorflow adalah cara yang ampuh untuk menyimpan data di sisi klien. Jika Anda belum melihatnya, sebaiknya baca tutorial MMD yang bermanfaat tentang topik tersebut. Artikel ini mengasumsikan beberapa pengetahuan dasar tentang API dan fitur-fiturnya. Bahkan jika Anda belum pernah melihat tensorflow sebelumnya, semoga demo dalam artikel ini akan memberi Anda gambaran tentang apa yang dapat dilakukan dengannya.

Demo kami adalah bukti konsep sederhana penerapan Intranet untuk sebuah perusahaan. Aplikasi ini akan memungkinkan karyawan mencari karyawan lain. Untuk memberikan pengalaman yang lebih cepat dan lebih cepat, {i>database<i} karyawan disalin ke komputer klien dan disimpan menggunakan IndexedDB. Demo ini hanya menyediakan penelusuran bergaya pelengkapan otomatis dan menampilkan satu kumpulan data karyawan, tetapi bagusnya setelah data ini tersedia di klien, kita juga dapat menggunakannya dalam beberapa cara lain. Berikut ini adalah garis besar dasar tentang apa yang perlu dilakukan aplikasi kita.

  1. Kita harus menyiapkan, dan menginisialisasi, sebuah instance dari IndexedDB. Sebagian besar cara ini mudah, tetapi membuatnya berfungsi di Chrome dan Firefox terbukti sedikit rumit.
  2. Kita perlu melihat apakah kita memiliki data, dan jika belum, mengunduhnya. Sekarang biasanya ini dilakukan melalui panggilan AJAX. Untuk demo ini, kami telah membuat class utilitas sederhana untuk membuat data palsu dengan cepat. Aplikasi harus mengenali kapan membuat data ini dan mencegah pengguna menggunakan data sampai saat itu. Operasi ini hanya perlu Anda lakukan satu kali. Saat berikutnya pengguna menjalankan aplikasi, ia tidak perlu melalui proses ini. Demo lanjutan akan menangani operasi sinkronisasi antara klien dan server, tetapi demo ini lebih berfokus pada aspek UI.
  3. Bila aplikasi sudah siap, kita kemudian bisa menggunakan kontrol Autocomplete UI jQuery untuk menyinkronkan dengan IndexedDB. Meskipun kontrol Autocomplete memungkinkan daftar dan array data dasar, kontrol tersebut memiliki API untuk memungkinkan sumber data apa pun. Kami akan menunjukkan bagaimana kita dapat menggunakannya untuk terhubung ke data IndexedDB.

Memulai

Kami memiliki beberapa bagian untuk demo ini, jadi untuk memulai lebih sederhana, mari kita lihat bagian HTML.

<form>
  <p>
    <label for="name">Name:</label> <input id="name" disabled> <span id="status"></span>
    </p>
</form>

<div id="displayEmployee"></div>

Tidak banyak, bukan? Ada tiga aspek utama terkait UI ini yang perlu diperhatikan. Pertama adalah kolom "nama" yang akan digunakan untuk pelengkapan otomatis. Pemuatan tersebut dinonaktifkan dan akan diaktifkan nanti melalui JavaScript. Span di sebelahnya digunakan selama seed awal untuk memberikan update kepada pengguna. Terakhir, div dengan id displayEmployee akan digunakan saat Anda memilih karyawan dari saran otomatis.

Sekarang mari kita lihat JavaScript. Ada banyak hal yang dapat dicerna di sini jadi kita akan membahasnya langkah demi langkah. Kode lengkap akan tersedia di akhir sehingga Anda dapat melihatnya secara keseluruhan.

Pertama - ada beberapa masalah awalan yang harus kami khawatirkan di antara browser yang mendukung IndexedDB. Berikut ini beberapa kode dari dokumentasi Mozilla yang dimodifikasi untuk memberikan alias sederhana bagi komponen IndexedDB inti yang dibutuhkan aplikasi kita.

window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;
var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;

Berikutnya, beberapa variabel global yang akan kita gunakan dalam demo:

var db;
var template;

Sekarang kita akan mulai dengan blok siap dokumen jQuery:

$(document).ready(function() {
  console.log("Startup...");
  ...
});

Demo kami menggunakan Handlebars.js untuk menampilkan detail karyawan. Itu tidak digunakan sampai nanti, tapi kita bisa melanjutkan dan mengompilasi {i>template<i} kita sekarang dan menghindarinya. Kami memiliki blok skrip yang disiapkan sebagai jenis yang dikenali Handlebar. Ini tidak terlalu mewah, tetapi membuatnya lebih mudah untuk menampilkan HTML dinamis.

<h2>, </h2>
Department: <br/>
Email: <a href='mailto:'></a>

Kode ini kemudian dikompilasi kembali ke dalam JavaScript kita seperti berikut:

//Create our template
var source = $("#employeeTemplate").html();
template = Handlebars.compile(source);

Sekarang mari mulai bekerja dengan IndexedDB. Pertama - kita membukanya.

var openRequest = indexedDB.open("employees", 1);

Membuka koneksi ke IndexedDB memberi kita akses untuk membaca dan menulis data, tetapi sebelum melakukannya, kita harus memastikan bahwa kita memiliki objectStore. ObjectStore seperti tabel database. Satu postingan mungkin memiliki banyak objectStores, masing-masing berisi kumpulan objek terkait. Demo kami sederhana dan hanya memerlukan satu objectStore yang kita sebut “employee”. Saat indexedDB dibuka untuk pertama kalinya, atau saat Anda mengubah versi dalam kode, peristiwa upgrade yang diperlukan akan dijalankan. Kita dapat menggunakan ini untuk menyiapkan objectStore.

// Handle setup.
openRequest.onupgradeneeded = function(e) {

  console.log("running onupgradeneeded");
  var thisDb = e.target.result;

  // Create Employee
  if(!thisDb.objectStoreNames.contains("employee")) {
    console.log("I need to make the employee objectstore");
    var objectStore = thisDb.createObjectStore("employee", {keyPath: "id", autoIncrement: true});
    objectStore.createIndex("searchkey", "searchkey", {unique: false});
  }

};

openRequest.onsuccess = function(e) {
  db = e.target.result;

  db.onerror = function(e) {
    alert("Sorry, an unforseen error was thrown.");
    console.log("***ERROR***");
    console.dir(e.target);
  };

  handleSeed();
};

Di blok pengendali peristiwa onupgradeneeded, kita memeriksa objectStoreNames, array penyimpanan objek, untuk melihat apakah objek tersebut berisi karyawan. Jika tidak, kita cukup melakukannya. Panggilan createIndex penting. Kita harus memberi tahu IndexedDB metode apa, di luar kunci, yang akan kita gunakan untuk mengambil data. Kita akan menggunakan satu yang disebut {i>searchkey<i}. Hal ini akan dijelaskan sebentar lagi.

Peristiwa onungradeneeded akan berjalan secara otomatis saat pertama kali menjalankan skrip. Setelah dieksekusi, atau dilewati dalam proses selanjutnya, pengendali onsuccess akan dijalankan. Kita memiliki pengendali error sederhana (dan jelek) yang ditentukan, lalu kita memanggil handleSeed.

Sebelum kita lanjutkan, mari kita tinjau apa yang terjadi di sini dengan cepat. Kita membuka {i>database<i}. Kita memeriksa untuk melihat apakah penyimpanan objek ada. Jika tidak, kita akan membuatnya. Terakhir, kita panggil fungsi bernama handleSeed. Sekarang mari kita alihkan perhatian ke bagian penyemenan data dalam demo kami.

Beri Aku Data!

Seperti yang disebutkan di bagian pengantar artikel ini, demo ini membuat ulang aplikasi bergaya Intranet yang perlu menyimpan salinan semua karyawan yang dikenal. Biasanya, langkah ini akan melibatkan pembuatan API berbasis server yang dapat menampilkan jumlah karyawan dan menyediakan cara bagi kita untuk mengambil kumpulan data. Anda dapat membayangkan layanan sederhana yang mendukung penghitungan awal dan menampilkan 100 orang sekaligus. Fungsi ini dapat berjalan secara asinkron di latar belakang saat pengguna sedang tidak melakukan aktivitas lain.

Untuk demo, kami melakukan sesuatu yang sederhana. Kita melihat jumlah objek, jika ada, yang kita miliki di IndexedDB. Jika jumlahnya di bawah angka tertentu, kami hanya akan membuat pengguna palsu. Jika tidak, kita dianggap selesai dengan bagian seed dan dapat mengaktifkan bagian pelengkapan otomatis dari demo. Mari kita lihat handleSeed.

function handleSeed() {
  // This is how we handle the initial data seed. Normally this would be via AJAX.

  db.transaction(["employee"], "readonly").objectStore("employee").count().onsuccess = function(e) {
    var count = e.target.result;
    if (count == 0) {
      console.log("Need to generate fake data - stand by please...");
      $("#status").text("Please stand by, loading in our initial data.");
      var done = 0;
      var employees = db.transaction(["employee"], "readwrite").objectStore("employee");
      // Generate 1k people
      for (var i = 0; i < 1000; i++) {
         var person = generateFakePerson();
         // Modify our data to add a searchable field
         person.searchkey = person.lastname.toLowerCase();
         resp = employees.add(person);
         resp.onsuccess = function(e) {
           done++;
           if (done == 1000) {
             $("#name").removeAttr("disabled");
             $("#status").text("");
             setupAutoComplete();
           } else if (done % 100 == 0) {
             $("#status").text("Approximately "+Math.floor(done/10) +"% done.");
           }
         }
      }
    } else {
      $("#name").removeAttr("disabled");
      setupAutoComplete();
    }
  };
}

Baris pertama agak rumit karena kita memiliki beberapa operasi yang dirantai satu sama lain, jadi mari kita uraikan:

db.transaction(["employee"], "readonly");

Tindakan ini akan membuat transaksi hanya baca yang baru. Semua operasi data dengan IndexedDB memerlukan semacam transaksi.

objectStore("employee");

Mendapatkan penyimpanan objek karyawan.

count()

Menjalankan count API - yang seperti yang bisa Anda tebak - melakukan penghitungan.

onsuccess = function(e) {

Dan setelah selesai - jalankan callback ini. Di dalam callback, kita bisa mendapatkan nilai hasil yang merupakan jumlah objek. Jika hitungannya nol, kita akan memulai proses seed.

Kita menggunakan div status yang disebutkan sebelumnya untuk memberi pesan kepada pengguna bahwa kita akan mulai mendapatkan data. Karena sifat IndexedDB, kami telah menyiapkan variabel sederhana, selesai, yang akan melacak penambahan. Kami memutar ulang dan menyisipkan orang palsu itu. Sumber fungsi tersebut tersedia di download, tetapi menampilkan objek yang terlihat seperti ini:

{
  firstname: "Random Name",
  lastname: "Some Random Last Name",
  department: "One of 8 random departments",
  email: "first letter of firstname+lastname@fakecorp.com"
}

Dengan sendirinya, hal ini sudah cukup untuk mendefinisikan seseorang. Tetapi kita memiliki persyaratan khusus agar dapat mencari data. tensorflow tidak menyediakan cara untuk mencari item dengan cara yang tidak peka huruf besar/kecil. Oleh karena itu, kita membuat salinan kolom lastname ke properti baru, yaitu kunci penelusuran. Jika Anda ingat, ini adalah kunci yang kami katakan harus dibuat sebagai indeks untuk data kami.

// Modify our data to add a searchable field
person.searchkey = person.lastname.toLowerCase();

Karena ini adalah modifikasi spesifik klien, dilakukan di sini, bukan di server {i>back-end<i} (atau dalam kasus kami, server {i>back-end<i} fiktif).

Untuk melakukan penambahan database dengan cara yang berperforma tinggi, Anda harus menggunakan kembali transaksi untuk semua batch operasi tulis. Jika Anda membuat transaksi baru untuk setiap operasi tulis, browser dapat menyebabkan operasi tulis disk untuk setiap transaksi, dan hal ini akan membuat performa Anda menjadi buruk saat menambahkan banyak item (misalnya "1 menit untuk menulis 1000 objek"-mengerikan).

Setelah seed selesai, bagian berikutnya dari aplikasi akan diaktifkan - setupAutoComplete.

Membuat Autocomplete

Sekarang untuk bagian yang menyenangkan - menghubungkan dengan plugin Autocomplete UI jQuery. Seperti kebanyakan UI jQuery, kita memulai dengan elemen HTML dasar dan menyempurnakannya dengan memanggil metode konstruktor di dalamnya. Kita telah memisahkan seluruh proses menjadi fungsi yang disebut setupAutoComplete. Mari kita lihat kode tersebut sekarang.

function setupAutoComplete() {

  //Create the autocomplete
  $("#name").autocomplete({
    source: function(request, response) {

      console.log("Going to look for "+request.term);

      $("#displayEmployee").hide();

      var transaction = db.transaction(["employee"], "readonly");
      var result = [];

      transaction.oncomplete = function(event) {
        response(result);
      };

      // TODO: Handle the error and return to it jQuery UI
      var objectStore = transaction.objectStore("employee");

      // Credit: http://stackoverflow.com/a/8961462/52160
      var range = IDBKeyRange.bound(request.term.toLowerCase(), request.term.toLowerCase() + "z");
      var index = objectStore.index("searchkey");

      index.openCursor(range).onsuccess = function(event) {
        var cursor = event.target.result;
        if(cursor) {
          result.push({
            value: cursor.value.lastname + ", " + cursor.value.firstname,
            person: cursor.value
          });
          cursor.continue();
        }
      };
    },
    minLength: 2,
    select: function(event, ui) {
      $("#displayEmployee").show().html(template(ui.item.person));
    }
  });

}

Bagian paling kompleks dari kode ini adalah pembuatan properti sumber. Kontrol Autocomplete UI jQuery memungkinkan Anda menentukan properti sumber yang dapat disesuaikan untuk memenuhi kebutuhan yang mungkin, bahkan data IndexedDB kami. API memberi Anda permintaan (pada dasarnya apa yang diketik ke dalam kolom formulir) dan callback respons. Anda bertanggung jawab untuk mengirim array hasil kembali ke callback tersebut.

Hal pertama yang kita lakukan adalah menyembunyikan div displayEmployee. Ini digunakan untuk menampilkan setiap karyawan, dan jika sudah dimuat sebelumnya, untuk menghapusnya. Sekarang kita bisa mulai mencari.

Kita memulai dengan membuat transaksi hanya baca, array yang disebut hasil, dan pengendali oncomplete yang hanya meneruskan hasilnya ke kontrol pelengkapan otomatis.

Untuk menemukan item yang cocok dengan input kita, mari kita gunakan tip oleh pengguna StackOverflow Fong-Wan Chau: Kita menggunakan rentang indeks berdasarkan input sebagai batas ujung bawah dan input ditambah huruf z sebagai batas rentang atas. Perhatikan juga bahwa kita menggunakan huruf kecil untuk istilah yang sesuai dengan data huruf kecil yang kita masukkan.

Setelah selesai - kita dapat membuka kursor (anggap saja seperti menjalankan kueri database) dan mengulangi hasilnya. Kontrol pelengkapan otomatis UI jQuery memungkinkan Anda menampilkan jenis data apa pun yang Anda inginkan, tetapi setidaknya memerlukan kunci nilai. Kita menetapkan nilai ini ke versi nama yang diformat dengan baik. Kami juga akan menampilkan seluruh pengguna. Anda akan melihat alasannya dalam satu detik. Pertama, berikut adalah screenshot penggunaan pelengkapan otomatis. Kita menggunakan tema Vader untuk UI jQuery.

Dengan sendirinya, ini cukup untuk mengembalikan hasil kecocokan tensorflow kami dengan pelengkapan otomatis. Namun, kita juga ingin mendukung tampilan detail kecocokan saat dipilih. Kami menentukan pengendali tertentu saat membuat pelengkapan otomatis yang menggunakan template Handlebars dari pembahasan sebelumnya.