IndexedDB के साथ डेटा बाइंडिंग यूज़र इंटरफ़ेस (यूआई) एलिमेंट

परिचय

IndexedDB, क्लाइंट साइड पर डेटा स्टोर करने का एक बेहतरीन तरीका है. अगर आपने अब तक इस बारे में नहीं पढ़ा है, तो हमारा सुझाव है कि आप इस विषय पर MDN के ट्यूटोरियल पढ़ें. इस लेख में, एपीआई और उनकी सुविधाओं के बारे में बुनियादी जानकारी दी गई है. भले ही, आपने पहले कभी IndexedDB नहीं देखा हो, लेकिन उम्मीद है कि इस लेख में दिए गए डेमो से आपको यह पता चल जाएगा कि इसका इस्तेमाल किस तरह किया जा सकता है.

हमारा डेमो, किसी कंपनी के लिए कॉन्सेप्ट के सबूत के तौर पर बनाया गया एक आसान इंटरनेट ऐप्लिकेशन है. इस ऐप्लिकेशन की मदद से, कर्मचारी दूसरे कर्मचारियों को खोज सकते हैं. तेज़ और बेहतर अनुभव देने के लिए, कर्मचारी के डेटाबेस को क्लाइंट की मशीन पर कॉपी किया जाता है और IndexedDB का इस्तेमाल करके सेव किया जाता है. इस डेमो में, ऑटोकंप्लीट स्टाइल की खोज और किसी एक कर्मचारी के रिकॉर्ड को दिखाने की सुविधा दी गई है. हालांकि, इस डेटा के क्लाइंट पर उपलब्ध होने के बाद, इसका इस्तेमाल कई अन्य तरीकों से भी किया जा सकता है. यहां बताया गया है कि हमारे ऐप्लिकेशन को क्या करना चाहिए.

  1. हमें IndexedDB का एक इंस्टेंस सेट अप और शुरू करना होगा. ज़्यादातर मामलों में यह आसान होता है, लेकिन इसे Chrome और Firefox, दोनों में काम कराने में थोड़ी मुश्किल आती है.
  2. हमें यह देखना होगा कि हमारे पास कोई डेटा है या नहीं. अगर नहीं है, तो उसे डाउनलोड करें. आम तौर पर, ऐसा AJAX कॉल के ज़रिए किया जाता है. अपने डेमो के लिए, हमने नकली डेटा को तुरंत जनरेट करने के लिए एक आसान यूटिलिटी क्लास बनाई है. ऐप्लिकेशन को यह पता लगाना होगा कि वह यह डेटा कब बना रहा है और तब तक उपयोगकर्ता को डेटा का इस्तेमाल करने से रोकना होगा. ऐसा एक ही बार करना होगा. अगली बार जब उपयोगकर्ता ऐप्लिकेशन चलाएगा, तो उसे इस प्रोसेस से गुज़रने की ज़रूरत नहीं होगी. ज़्यादा बेहतर डेमो, क्लाइंट और सर्वर के बीच सिंक करने की प्रोसेस को मैनेज करेगा. हालांकि, इस डेमो में यूज़र इंटरफ़ेस (यूआई) के पहलुओं पर ज़्यादा फ़ोकस किया गया है.
  3. ऐप्लिकेशन तैयार होने के बाद, IndexedDB के साथ सिंक करने के लिए, jQuery UI के ऑटोकंप्लीट कंट्रोल का इस्तेमाल किया जा सकता है. ऑटोकंप्लीट कंट्रोल की मदद से, डेटा की बुनियादी सूचियां और ऐरे बनाए जा सकते हैं. साथ ही, इसमें किसी भी डेटा सोर्स को अनुमति देने के लिए एपीआई भी है. हम आपको दिखाएंगे कि IndexedDB डेटा से कनेक्ट करने के लिए, इसका इस्तेमाल कैसे किया जा सकता है.

शुरू करें

इस डेमो में कई हिस्से हैं. इसलिए, आसानी से शुरुआत करने के लिए, आइए एचटीएमएल वाले हिस्से पर नज़र डालें.

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

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

बहुत ज़्यादा नहीं, है न? इस यूज़र इंटरफ़ेस के तीन मुख्य पहलु हैं जिन पर हम ध्यान देते हैं. पहला फ़ील्ड, "name" है. इसका इस्तेमाल, अपने-आप पूरा होने की सुविधा के लिए किया जाएगा. यह बंद होकर लोड होता है और बाद में JavaScript की मदद से चालू किया जाता है. इसके बगल में मौजूद स्पैन का इस्तेमाल, शुरुआती सीड के दौरान उपयोगकर्ता को अपडेट देने के लिए किया जाता है. आखिर में, अपने-आप सुझाव मिलने की सुविधा से किसी कर्मचारी को चुनने पर, displayEmployee आईडी वाले div का इस्तेमाल किया जाएगा.

अब आइए, JavaScript को देखें. यहां बहुत कुछ समझना है, इसलिए हम इसे सिलसिलेवार तरीके से समझेंगे. पूरा कोड आखिर में उपलब्ध होगा, ताकि आप उसे पूरा देख सकें.

सबसे पहले - IndexedDB के साथ काम करने वाले ब्राउज़र में, प्रीफ़िक्स से जुड़ी कुछ समस्याएं हैं जिनकी हमें चिंता करनी होगी. यहां Mozilla के दस्तावेज़ का कुछ कोड दिया गया है. इसमें बदलाव करके, IndexedDB के उन मुख्य कॉम्पोनेंट के लिए आसान उपनाम दिए गए हैं जिनकी हमारे ऐप्लिकेशन को ज़रूरत है.

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

इसके बाद, कुछ ग्लोबल वैरिएबल हैं जिनका इस्तेमाल हम पूरे डेमो में करेंगे:

var db;
var template;

अब हम jQuery document ready ब्लॉक से शुरुआत करेंगे:

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

हमारे डेमो में, कर्मचारी की जानकारी दिखाने के लिए Handlebars.js का इस्तेमाल किया गया है. इसका इस्तेमाल बाद में किया जाता है. हालांकि, हम अभी अपने टेंप्लेट को कंपाइल कर सकते हैं और इसे हटा सकते हैं. हमारे पास एक स्क्रिप्ट ब्लॉक है, जिसे हैंडलबार के तौर पर सेट अप किया गया है. यह बहुत शानदार नहीं है, लेकिन इससे डाइनैमिक एचटीएमएल को आसानी से दिखाया जा सकता है.

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

इसके बाद, इसे हमारे JavaScript में इस तरह से फिर से कंपाइल किया जाता है:

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

अब चलिए, IndexedDB का इस्तेमाल शुरू करते हैं. सबसे पहले - हम इसे खोलते हैं.

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

IndexedDB से कनेक्शन खोलने पर, हमें डेटा पढ़ने और उसमें बदलाव करने का ऐक्सेस मिलता है. हालांकि, ऐसा करने से पहले, हमें यह पक्का करना होगा कि हमारे पास objectStore है. ऑब्जेक्टस्टोर, डेटाबेस टेबल की तरह होता है. किसी IndexedDB में कई objectStore हो सकते हैं. हर objectStore में मिलते-जुलते ऑब्जेक्ट का कलेक्शन होता है. हमारा डेमो आसान है और इसमें सिर्फ़ एक objectStore की ज़रूरत होती है, जिसे हम “employee” कहते हैं. जब indexedDB पहली बार खोला जाता है या कोड में वर्शन बदला जाता है, तो onupgradeneeded इवेंट चलता है. हम इसका इस्तेमाल अपने 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
();
};

onupgradeneeded इवेंट हैंडलर ब्लॉक में, हम objectStoreNames की जांच करते हैं. यह ऑब्जेक्ट स्टोर का कलेक्शन होता है. इससे यह पता चलता है कि इसमें employee शामिल है या नहीं. अगर ऐसा नहीं है, तो हम इसे ऐसा करने के लिए कहेंगे. createIndex कॉल ज़रूरी है. हमें IndexedDB को यह बताना होगा कि डेटा को वापस पाने के लिए, हम कुंजियों के अलावा किन तरीकों का इस्तेमाल करेंगे. हम searchkey नाम के एक फ़ंक्शन का इस्तेमाल करेंगे. इस बारे में थोड़ी देर में बताया जाएगा.

स्क्रिप्ट को पहली बार चलाने पर, onungradeneeded इवेंट अपने-आप चल जाएगा. इसे लागू करने या आने वाले समय में इसे स्किप करने के बाद, onsuccess हैंडलर चलाया जाता है. हमने गड़बड़ी को मैनेज करने के लिए एक आसान (और खराब) तरीका तय किया है. इसके बाद, हम handleSeed को कॉल करते हैं.

आगे बढ़ने से पहले, आइए एक नज़र डालते हैं कि यहां क्या हो रहा है. हम डेटाबेस खोलते हैं. हम यह जांच करते हैं कि हमारा ऑब्जेक्ट स्टोर मौजूद है या नहीं. अगर ऐसा नहीं है, तो हम उसे बनाते हैं. आखिर में, हम handleSeed नाम के फ़ंक्शन को कॉल करते हैं. अब डेमो के डेटा सीडिंग वाले हिस्से पर ध्यान दें.

मुझे डेटा चाहिए!

इस लेख के शुरुआती हिस्से में बताया गया है कि इस डेमो में, एक ऐसा इन्टरनेट स्टाइल ऐप्लिकेशन बनाया जा रहा है जिसमें सभी कर्मचारियों की जानकारी की कॉपी सेव की जानी है. आम तौर पर, इसके लिए सर्वर-आधारित एपीआई बनाना होगा, जो कर्मचारियों की संख्या दिखा सके. साथ ही, हमें रिकॉर्ड के बैच वापस पाने का तरीका भी दे सके. एक ऐसी आसान सेवा की कल्पना की जा सकती है जो शुरुआती संख्या के साथ काम करती है और एक बार में 100 लोगों की जानकारी दिखाती है. जब उपयोगकर्ता कुछ और काम कर रहा हो, तब यह बैकग्राउंड में असिंक्रोनस तरीके से चल सकता है.

हम डेमो के लिए, कुछ आसान काम करते हैं. इससे हमें पता चलता है कि हमारे IndexedDB में कितने ऑब्जेक्ट हैं. अगर यह संख्या तय सीमा से कम है, तो हम नकली उपयोगकर्ता बना देंगे. अगर ऐसा नहीं होता है, तो हम मान लेंगे कि आपने सीड डेटा का इस्तेमाल कर लिया है. इसके बाद, हम डेमो के लिए अपने-आप पूरा होने की सुविधा चालू कर सकते हैं. आइए, 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
();
   
}
 
};
}

पहली लाइन थोड़ी मुश्किल है, क्योंकि इसमें एक-दूसरे से जुड़ी कई कार्रवाइयां हैं. इसलिए, आइए इसे अलग-अलग हिस्सों में बांटकर देखें:

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

इससे, रीड ओनली ट्रांज़ैक्शन बन जाता है. IndexedDB के साथ डेटा के सभी ऑपरेशन के लिए, किसी तरह का ट्रांज़ैक्शन ज़रूरी है.

objectStore("employee");

कर्मचारी का ऑब्जेक्ट स्टोर पाएं.

count()

count API चलाएं, जो अनुमान लगाया जा सकता है कि गिनती करता है.

onsuccess = function(e) {

इसके बाद, इस कॉलबैक को लागू करें. कॉलबैक में, हमें नतीजे की वैल्यू मिल सकती है, जो ऑब्जेक्ट की संख्या होती है. अगर गिनती शून्य थी, तो हम बीज की प्रोसेस शुरू करते हैं.

हम पहले बताए गए स्टेटस डिव का इस्तेमाल करके, उपयोगकर्ता को यह मैसेज देते हैं कि हम डेटा इकट्ठा करना शुरू कर रहे हैं. IndexedDB के असाइनॉन्स (एक साथ होने वाली प्रोसेस) की वजह से, हमने एक आसान वैरिएबल सेट अप किया है. इसका नाम 'हो गया' है. यह वैरिएबल, जोड़े गए आइटम को ट्रैक करेगा. हम नकली लोगों को लूप में डालते हैं. उस फ़ंक्शन का सोर्स, डाउनलोड में उपलब्ध है, लेकिन यह ऐसा ऑब्जेक्ट दिखाता है:

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

इससे किसी व्यक्ति के बारे में काफ़ी जानकारी मिल जाती है. हालांकि, अपने डेटा को खोजने के लिए, हमें एक खास शर्त पूरी करनी होगी. IndexedDB में, छोटे-बड़े अक्षरों के हिसाब से आइटम खोजने का विकल्प नहीं होता. इसलिए, हम lastname फ़ील्ड की कॉपी को नई प्रॉपर्टी, searchkey में बनाते हैं. अगर आपको याद है, तो यह वह कुंजी है जिसके बारे में हमने कहा था कि इसे हमारे डेटा के लिए इंडेक्स के तौर पर बनाया जाना चाहिए.

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

यह क्लाइंट के हिसाब से बदलाव है. इसलिए, इसे बैक-एंड सर्वर (या हमारे मामले में, काल्पनिक बैक-एंड सर्वर) के बजाय यहां किया जाता है.

डेटाबेस में डेटा जोड़ने की प्रोसेस को बेहतर तरीके से पूरा करने के लिए, आपको एक साथ कई रिकॉर्ड जोड़ने के लिए, ट्रांज़ैक्शन का फिर से इस्तेमाल करना चाहिए. अगर हर बार डेटा सेव करने के लिए नया लेन-देन बनाया जाता है, तो हो सकता है कि ब्राउज़र हर लेन-देन के लिए डिस्क पर डेटा सेव करे. इससे, बहुत सारे आइटम जोड़ने पर आपकी परफ़ॉर्मेंस बहुत खराब हो जाएगी. उदाहरण के लिए, "1,000 ऑब्जेक्ट को सेव करने में एक मिनट".

सीड पूरा होने के बाद, हमारे ऐप्लिकेशन का अगला हिस्सा चालू हो जाता है - setupAutoComplete.

अपने-आप पूरा होने की सुविधा बनाना

अब मज़ेदार हिस्सा - jQuery UI ऑटोकंप्लीट प्लग इन से जोड़ना. ज़्यादातर jQuery यूज़र इंटरफ़ेस की तरह, हम एक बुनियादी एचटीएमएल एलिमेंट से शुरू करते हैं और उस पर कन्स्ट्रक्टर का तरीका कॉल करके उसे बेहतर बनाते हैं. हमने पूरी प्रोसेस को setupAutoComplete नाम के फ़ंक्शन में शामिल किया है. आइए अब उस कोड पर नज़र डालते हैं.

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));
   
}
 
});

}

इस कोड का सबसे मुश्किल हिस्सा, सोर्स प्रॉपर्टी बनाना है. jQuery UI के ऑटोकंप्लीट कंट्रोल की मदद से, एक सोर्स प्रॉपर्टी तय की जा सकती है. इसे किसी भी ज़रूरत के हिसाब से कस्टमाइज़ किया जा सकता है. यहां तक कि, IndexedDB डेटा को भी कस्टमाइज़ किया जा सकता है. एपीआई आपको अनुरोध (फ़ॉर्म फ़ील्ड में टाइप किया गया अनुरोध) और जवाब का कॉलबैक देता है. उस कॉलबैक में नतीजों का कलेक्शन भेजने की ज़िम्मेदारी आपकी है.

सबसे पहले, हम displayEmployee div को छिपा देते हैं. इसका इस्तेमाल किसी कर्मचारी को दिखाने के लिए किया जाता है. अगर कोई कर्मचारी पहले से लोड है, तो उसे हटा दिया जाता है. अब हम खोज शुरू कर सकते हैं.

हम रीड-ओनली ट्रांज़ैक्शन, नतीजा नाम का कलेक्शन, और oncomplete हैंडलर बनाकर शुरू करते हैं. यह हैंडलर, नतीजे को अपने-आप पूरा होने वाले कंट्रोल को पास करता है.

अपने इनपुट से मैच होने वाले आइटम ढूंढने के लिए, StackOverflow के उपयोगकर्ता फ़ॉन्ग-वान चाऊ के सुझाव का इस्तेमाल करें: हम इनपुट के आधार पर इंडेक्स रेंज का इस्तेमाल, निचले हिस्से की सीमा के तौर पर करते हैं. साथ ही, इनपुट के साथ अक्षर z का इस्तेमाल, ऊपरी रेंज की सीमा के तौर पर करते हैं. ध्यान दें कि हम आपके डाले गए लोअरकेस डेटा से मैच करने के लिए, शब्द को लोअरकेस में बदल देते हैं.

इसके बाद, हम कर्सर खोल सकते हैं (इसे डेटाबेस क्वेरी चलाने की तरह समझें) और नतीजों को दोहरा सकते हैं. jQuery UI के ऑटोकंप्लीट कंट्रोल की मदद से, अपनी पसंद का कोई भी डेटा दिखाया जा सकता है. हालांकि, इसके लिए कम से कम वैल्यू की ज़रूरत होती है. हम वैल्यू को नाम के अच्छे फ़ॉर्मैट वाले वर्शन पर सेट करते हैं. हम पूरे व्यक्ति को भी वापस लाते हैं. आपको इसका जवाब कुछ ही समय में मिल जाएगा. सबसे पहले, यहां ऑटोकंप्लीट की सुविधा के काम करने का स्क्रीनशॉट दिया गया है. हम jQuery UI के लिए Vader थीम का इस्तेमाल कर रहे हैं.

यह अपने-आप ही, ऑटोकंप्लीट की सुविधा में IndexedDB से मैच होने वाले नतीजे दिखाने के लिए काफ़ी है. हालांकि, हम मैच चुनने पर, उसकी ज़्यादा जानकारी वाला व्यू भी दिखाना चाहते हैं. हमने अपने-आप पूरा होने वाली सूची बनाते समय, एक चुनिंदा हैंडलर तय किया है. यह हैंडलर, पहले से मौजूद हैंडलबार टेंप्लेट का इस्तेमाल करता है.