Ciekawym trendem, jaki można zaobserwować w standardzie HTML5 jest możliwość składowania coraz większej ilości danych po stronie klienta. Przestarzałe ciasteczka zastępowane są przez znacznie lepszy mechanizm localStorage. To jednak nie wszystko. Pojawia się także możliwość składowania danych w dokumentowej bazie
IndexedDb. Podobnie jak w WebStorage, dane zapisywane są dla danej strony internetowej i tylko z jej poziomu mogą być odczytane. Tym razem możemy jednak składować całe obiekty JavaScriptowe, a nie tylko ciągi znaków. Co więcej, zgodnie z
dokumentacją IndexedDb, pojemność takiej bazy praktycznie ograniczona jest jedynie miejscem na dysku. Same zapytania wykonywane są w sposób asynchroniczny, programista zatem musi ich wynik obsłużyć w callbackach.
Pracę z indexedDb, technologią mocno eksperymentalną, nad którą prace cały czas trwają, warto rozpocząć od przypisania sobie dla wygody każdej z możliwych implementacji bazy do jednej zmiennej.
window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
Jak widać ostatnim silnikiem przeglądarek, który nie wspiera indexedDb jest Presto, na którym tworzona była Opera (obecnie opera ma także być tworzona w oparciu o webkit, więc problem znika dla nowszych wersji).
Zapisywanie obiektów do bazy odbywa się poprzez odpowiednie callbacki.
$("#store").click(function(){
var value = $("#msg").val();
var request = indexedDB.open("MyDb", 1); //1
request.onsuccess = function (evt) {
db = request.result;
var transaction = db.transaction("messages", "readwrite"); //2
var objectStore = transaction.objectStore("messages"); //3
var req = objectStore.add({ date: new Date().toLocaleString(), message: value });
req.onsuccess = function (evt) {
console.log('successfully saved');
};
};
request.onerror = function (evt) {
console.dir(evt);
};
request.onupgradeneeded = function (evt) {
//...
};
});
Ważne są trzy linie:
- Otwieramy bazę danych o nazwie MyDb, opcjonalnie jako drugi parametr podajemy wersję schematu bazy, do której chcemy się łączyć
- Pierwszy argument może być także tablicą i jest listą kontenerów, na których będzie wykonywana transakcja
- Żeby dodać obiekt do pojedynczego kontenera potrzebujemy obiekt typu objectStore, o nazwie messages
Powyższy kod sprawdza się pod warunkiem, że mamy już utworzoną bazę i kontener, w przeciwnym wypadku wywołana zostanie funkcja
onupgradeneeded.
request.onupgradeneeded = function (evt) {
var objectStore = evt.currentTarget.result.createObjectStore("messages",
{ keyPath: "id", autoIncrement: true
});
objectStore.createIndex("date", "date", { unique: false });
objectStore.createIndex("message", "message", { unique: false });
};
Tworzymy
objectStore o nazwie messages, posiadający unikalny autoinkrementujący się identyfikator. Dodatkowo, na potrzeby wyszukiwania, można utworzyć dwa indeksy.
Odczyt danych
Podczas odczytu wykorzystuje się indeksy, zwracające wartość i wskaźnik na następny element z kolekcji.
$("#restore").click(function(){
var request = indexedDB.open("MyDb");
request.onsuccess = function(evt){
var db = request.result;
var transaction = db.transaction("messages");
var objectStore = transaction.objectStore("messages");
var req = objectStore.openCursor();
req.onsuccess = function(evt){
var cursor = evt.target.result;
if(cursor){
var obj = cursor.value;
var li = $("<li>");
li.html(obj.date + ", " + obj.message);
items.append(li);
cursor.continue();
}
}
}
});
Usuwanie
Korzystając z unikalnych indeksów, usuwanie staje się bardzo proste.Na przykład dla elementu o indeksie
1
$("#removeFirst").click(function(){
var request = indexedDB.open("MyDb");
request.onsuccess = function (evt) {
db = request.result;
var req = db.transaction(["messages"], "readwrite")
.objectStore("messages").delete(1);
}
});
Na koniec warto wspomnieć o tym, jak wygodnie można oglądać stan bazy w przeglądarce
Google Chrome, w zakładce
Resources.