Arbeiten mit IndexedDB - Teil 3

Willkommen zu Finale Teil meiner IndexedDB-Serie. Als ich mit dieser Serie begann, wollte ich eine Technologie erklären, die nicht immer die… freundlichste ist, mit der man arbeiten kann. Als ich letztes Jahr zum ersten Mal mit IndexedDB gearbeitet habe, war meine erste Reaktion etwas negativ ("etwas negativ", ähnlich wie das Universum "etwas alt" ist). Es war eine lange Reise, aber ich fühle mich endlich etwas wohl mit der Arbeit mit IndexedDB und respektiere, was es erlaubt. Es ist immer noch eine Technologie, die nicht überall verwendet werden kann (es wurde leider nicht zu iOS7 hinzugefügt), aber ich glaube wirklich, dass es eine Technologie ist, die die Leute heute erlernen und nutzen können.

In diesem letzten Artikel werden wir einige zusätzliche Konzepte demonstrieren, die auf der "vollständigen" Demo aufbauen, die wir im letzten Artikel erstellt haben. Um klar zu sein, du Muss Wenn Sie sich in die Serie einarbeiten, wird es schwierig sein, diesen Eintrag zu verfolgen, daher möchten Sie vielleicht auch Teil eins überprüfen.


Daten zählen

Beginnen wir mit etwas Einfachem. Stellen Sie sich vor, Sie möchten Ihren Daten Paging hinzufügen. Wie würden Sie eine Zählung Ihrer Daten erhalten, damit Sie diese Funktion richtig handhaben können? Ich habe dir schon gezeigt, wie du bekommen kannst alles Ihre Daten und sicherlich können Sie dies als eine Möglichkeit verwenden, um Daten zu zählen, aber dazu müssen Sie alles abrufen. Wenn Ihre lokale Datenbank sehr groß ist, kann dies langsam sein. Glücklicherweise bietet die IndexedDB-Spezifikation eine wesentlich einfachere Methode.

Die count () -Methode, die in einem objectStore ausgeführt wird, gibt eine Anzahl von Daten zurück. Wie alles andere, das wir getan haben, wird dies asynchron sein, aber Sie können den Code auf einen Aufruf vereinfachen. Für unsere Notizdatenbank habe ich eine Funktion geschrieben doCount () das macht genau das:

function doCount () db.transaction (["note"], "readonly"). objectStore ("note"). count (). onsuccess = function (Ereignis) $ ("# sizeSpan"). text ("( "+ event.target.result +" Notes Total) "); ; 

Denken Sie daran - wenn der Code oben etwas schwer zu befolgen ist, können Sie ihn in mehrere Blöcke aufteilen. Siehe die früheren Artikel, wo ich das demonstriert habe. Dem Ergebnis-Handler wird ein Ergebniswert übergeben, der die Gesamtanzahl der im Geschäft verfügbaren Objekte darstellt. Ich habe die Benutzeroberfläche unserer Demo so geändert, dass sie eine leere Spanne im Header enthält.

Beachten Sie die Datenbank 

Das letzte, was ich tun muss, ist einfach einen Aufruf an doCount hinzuzufügen, wenn die Anwendung gestartet wird und nachdem ein Vorgang zum Hinzufügen oder Löschen ausgeführt wurde. Hier ist ein Beispiel aus dem Erfolgs-Handler zum Öffnen der Datenbank.

openRequest.onsuccess = Funktion (e) db = e.target.result; db.onerror = function (event) // Generischer Fehlerhandler für alle Fehler, die auf die // Anforderungen dieser Datenbank gerichtet sind! alert ("Datenbankfehler:" + event.target.errorCode); ; displayNotes (); doCount (); ;

Das vollständige Beispiel finden Sie in der von Ihnen heruntergeladenen ZIP-Datei fulldemo2. (Als FYI, fulldemo1 ist die Bewerbung wie am Ende des vorherigen Artikels.)


Während der Eingabe filtern

Für unser nächstes Feature fügen wir der Notizenliste einen Basisfilter hinzu. In den vorangegangenen Artikeln dieser Serie habe ich die Funktionsweise von IndexedDB behandelt nicht Suche nach freiem Formular ermöglichen. Sie können den Inhalt (nicht so leicht) durchsuchen enthält ein Stichwort Mit der Kraft der Bereiche ist es jedoch leicht, das Matching am Anfang einer Zeichenfolge zu unterstützen.

Wenn Sie sich daran erinnern, können wir mit einem Bereich Daten aus einem Geschäft abrufen, das entweder mit einem bestimmten Wert beginnt, mit einem Wert endet oder dazwischen liegt. Wir können dies verwenden, um einen Basisfilter gegen den Titel unserer Notizfelder zu implementieren. Zuerst müssen wir einen Index für diese Eigenschaft hinzufügen. Denken Sie daran, dass dies nur in dem aufgerufenen Ereignis möglich ist.

 if (! thisDb.objectStoreNames.contains ("note")) console.log ("Ich muss den Hinweis objectstore erstellen"); objectStore = thisDb.createObjectStore ("note", keyPath: "id", autoIncrement: true); objectStore.createIndex ("title", "title", unique: false); 

Als Nächstes fügte ich der Benutzeroberfläche ein einfaches Formularfeld hinzu:


Dann fügte ich dem Feld einen "keyup" -Handler hinzu, so dass ich während der Eingabe sofort Aktualisierungen sehen konnte.

$ ("# filterField"). on ("keyup", Funktion (e) var filter = $ (this) .val (); displayNotes (filter););

Beachten Sie, wie ich displayNotes anrufe. Dies ist die gleiche Funktion, die ich zuvor verwendet habe, um alles anzuzeigen. Ich werde es aktualisieren, um sowohl eine Aktion "Get All" als auch eine Aktion "Get gefiltert" zu unterstützen. Lass uns einen Blick darauf werfen.

Funktion displayNotes (Filter) var transaction = db.transaction (["note"], "readonly"); var content = ""; transaction.oncomplete = function (event) $ (" # noteList "). html (content);; var handleResult = function (event) var Cursor = event.target.result; if (Cursor) content + = ""; Inhalt + =""; Inhalt + =""; Inhalt + =""; Cursor.continue (); else content + ="
TitelAktualisierte&
"+ cursor.value.title +""+ dtFormat (cursor.value.updated) +"Bearbeiten Löschen
";; var objectStore = transaction.objectStore (" note "); if (filter) // Bildnachweis: http://stackoverflow.com/a/8961462/52160 var range = IDBKeyRange.bound (filter, filter + "\ uffff"); var index = objectStore.index ("title"); index.openCursor (range) .onsuccess = handleResult; else objectStore.openCursor (). onsuccess = handleResult;

Die einzige Änderung ist hier ganz unten. Wenn Sie einen Cursor mit oder ohne Bereich öffnen, erhalten Sie dieselbe Art von Ereignisbehandlungsergebnis. Das ist praktisch, da es dieses Update so trivial macht. Der einzige komplexe Aspekt besteht darin, das Sortiment tatsächlich zu bauen. Beachten Sie, was ich hier gemacht habe. Die Eingabe filter ist die Eingabe des Benutzers. So stellen Sie sich vor, das ist "The". Wir möchten Notizen mit einem Titel finden, der mit "The" beginnt und in einem beliebigen Zeichen endet. Dies kann erreicht werden, indem einfach das entfernte Ende des Bereichs auf ein hohes ASCII-Zeichen gesetzt wird. Ich kann diese Idee nicht anerkennen. Siehe den StackOverflow-Link im Code für die Zuordnung.

Diese Demo finden Sie im fulldemo3 Mappe. Beachten Sie, dass dies eine neue Datenbank verwendet. Wenn Sie also die vorherigen Beispiele ausgeführt haben, ist diese beim ersten Ausführen leer.

Während dies funktioniert, hat es ein kleines Problem. Stellen Sie sich eine Notiz mit dem Titel "Saints Rule" vor. (Weil sie das tun. Nur sagen.) Am wahrscheinlichsten werden Sie versuchen, nach "Heiligen" zu suchen. Wenn Sie dies tun, funktioniert der Filter nicht, da die Groß- und Kleinschreibung beachtet wird. Wie können wir das umgehen??

Eine Möglichkeit ist, einfach eine Kopie unseres Titels in Kleinbuchstaben zu speichern. Dies ist relativ einfach zu machen. Zuerst habe ich den Index geändert, um eine neue Eigenschaft zu verwenden titlelc.

 objectStore.createIndex ("titlelc", "titlelc", unique: false);

Dann habe ich den Code, in dem Notizen gespeichert sind, geändert, um eine Kopie des Felds zu erstellen:

$ ("# saveNoteButton"). on ("click") function () var title = $ ("# title"). val (); var body = $ ("# body"). val (); var-Taste = $ ("# key"). val (); var titlelc = title.toLowerCase (); var t = db.transaction (["note"], "readwrite"); if (key === "")  t.objectStore ("note") .add (title: title, body: body, aktualisiert: neues Datum (), titlelc: titlelc); else t.objectStore ("note") .put (title: title, body: body, aktualisiert: new Date (), id: Number (Schlüssel), titlelc: titlelc);

Schließlich habe ich die Suche so geändert, dass die Benutzereingaben einfach in Kleinbuchstaben eingegeben werden. Wenn Sie "Saints" eingeben, funktioniert es genauso gut wie "Saints".

 filter = filter.toLowerCase (); var range = IDBKeyRange.bound (filter, filter + "\ uffff"); var index = objectStore.index ("titlelc");

Das ist es. Sie finden diese Version als fulldemo4.


Mit Array-Eigenschaften arbeiten

Für unsere abschließende Verbesserung füge ich unserer Notiz-Anwendung eine neue Funktion hinzu - Tagging. Dieser Wille
Sie können eine beliebige Anzahl von Tags hinzufügen (Schlüsselwörter, die die Notiz beschreiben), damit Sie später andere finden können
Notizen mit demselben Tag. Tags werden als Array gespeichert. Das ist an sich keine so große Sache. Ich habe am Anfang dieser Serie erwähnt, dass Sie Arrays problemlos als Eigenschaften speichern können. Was etwas komplexer ist, ist die Suche. Beginnen wir damit, es so zu machen, dass Sie einer Notiz Tags hinzufügen können.

Zuerst habe ich mein Notizformular geändert, um ein neues Eingabefeld zu erhalten. Auf diese Weise kann der Benutzer Tags eingeben, die durch ein Komma getrennt sind:


Ich kann dies speichern, indem Sie einfach meinen Code aktualisieren, der die Erstellung / Aktualisierung von Notizen abwickelt.

 var tags = []; var tagString = $ ("# tags"). val (); if (tagString.length) tags = tagString.split (",");

Beachten Sie, dass ich den Wert standardmäßig auf ein leeres Array stelle. Ich fülle es nur dann aus, wenn Sie etwas eingegeben haben. Das Speichern ist so einfach wie das Anhängen an das Objekt, das wir an IndexedDB übergeben:

 if (Schlüssel === "") t.objectStore ("note") .add (title: title, body: body, aktualisiert: new Date (), titlelc: titlelc, tags: tags);  else t.objectStore ("note") .put (title: title, body: body, aktualisiert: new Date (), id: Number (Schlüssel), titlelc: titlelc, tags: tags); 

Das ist es. Wenn Sie einige Notizen schreiben und die Registerkarte Ressourcen von Chrome öffnen, können Sie tatsächlich die gespeicherten Daten sehen.


Jetzt fügen wir der Ansicht Tags hinzu, wenn Sie eine Notiz anzeigen. Für meine Bewerbung habe ich mich für einen einfachen Anwendungsfall entschieden. Wenn eine Notiz angezeigt wird, wenn es Tags gibt, liste ich sie auf. Jedes Tag wird ein Link sein. Wenn Sie auf diesen Link klicken, zeige ich Ihnen eine Liste verwandter Notizen mit demselben Tag. Sehen wir uns zuerst diese Logik an.

Funktion displayNote (id) var transaction = db.transaction (["note"]); var objectStore = transaction.objectStore ("Hinweis"); var request = objectStore.get (id); request.onsuccess = Funktion (Ereignis) var note = request.result; var content = "

"+ note.title +"

"; if (note.tags.length> 0) content + ="Stichworte: "; note.tags.forEach (Funktion (elm, idx, arr) content + =" "+ elm +" ";); content + ="
"; content + ="

"+ note.body +"

"; I $ noteDetail.html (content) .show (); $ noteForm.hide ();;

Diese Funktion (ein neuer Zusatz zu unserer Anwendung) behandelt den Notizanzeigecode, der formal an das Klickereignis der Tabellenzelle gebunden ist. Ich brauchte eine abstraktere Version des Codes, damit dieser Zweck erfüllt wird. Meistens ist es dasselbe, aber beachten Sie die Logik, um die Länge der Tags-Eigenschaft zu überprüfen. Wenn das Array nicht leer ist, wird der Inhalt mit einer einfachen Liste von Tags aktualisiert. Jeder von ihnen ist mit einer bestimmten Klasse verbunden, die ich später zum Nachschlagen verwenden werde. Ich habe auch ein div hinzugefügt, um diese Suche zu erledigen.


An diesem Punkt habe ich die Möglichkeit, Markierungen zu einer Notiz hinzuzufügen und sie später anzuzeigen. Ich habe auch geplant, dass der Benutzer auf diese Tags klicken kann, um andere Notizen mit demselben Tag zu finden. Nun kommt der komplexe Teil.

Sie haben gesehen, wie Sie Inhalte basierend auf einem Index abrufen können. Aber wie funktioniert das mit den Array-Eigenschaften? Es stellt sich heraus, dass die Spezifikation ein spezielles Flag für den Umgang damit hat: multiEntry. Wenn Sie einen Array-basierten Index erstellen, müssen Sie diesen Wert auf true setzen. So behandelt meine Anwendung es:

objectStore.createIndex ("tags", "tags", unique: false, multiEntry: true);

Das bewältigt den Speicheraspekt gut. Lassen Sie uns jetzt über die Suche sprechen. Hier ist der Click-Handler für die Tag-Link-Klasse:

$ (document) .on ("click", ".tagLookup"), Funktion (e) var tag = e.target.text; var parentNote = $ (this) .data ("noteid"); var doneOne = false; var content = "Verwandte Hinweise:
"; var transaction = db.transaction ([" note "]," readonly "); var objectStore = transaction.objectStore (" note "); var tagIndex = objectStore.index (" tags "); var bereich = IDBKeyRange.only (Tag); transaction.oncomplete = Funktion (Ereignis) if (! doneOne) content + = "Dieses Tag wurde von keiner anderen Notiz verwendet."; content + = "

"; $ (" # relatedNotesDisplay "). html (content);; var handleResult = function (event) var Cursor = event.target.result; if (Cursor) if (cursor.value.id! = parentNote) doneOne = true; content + = ""+ cursor.value.title +"
"; cursor.continue ();; tagIndex.openCursor (range) .onsuccess = handleResult;);

Es gibt ziemlich viel hier - aber ehrlich gesagt - es ist dem sehr ähnlich, was wir zuvor besprochen haben. Wenn Sie auf ein Tag klicken, beginnt mein Code mit dem Text des Links für den Tag-Wert. Ich erstelle meine Transaktions-, Objectstore- und Indexobjekte, wie Sie sie zuvor gesehen haben. Der Bereich ist diesmal neu. Anstatt einen Bereich von etwas und zu etwas zu erstellen, können Sie mit der only () - api angeben, dass wir einen Bereich mit nur einem Wert wünschen. Und ja - das kam mir auch komisch vor. Aber es funktioniert super. Sie können sehen, dass wir den Cursor öffnen und wir können die Ergebnisse wie zuvor durchlaufen. Es gibt etwas zusätzlichen Code, um Fälle zu behandeln, in denen möglicherweise keine Übereinstimmungen vorhanden sind. Ich nehme auch das zur Kenntnis Original Anmerkung, d. h. diejenige, die Sie gerade anzeigen, damit ich sie nicht so gut ausstelle. Und das ist es wirklich. Ich habe einen letzten Code, der Klickereignisse in diesen verwandten Notizen behandelt, damit Sie sie problemlos anzeigen können:

$ (document) .on ("click", ".loadNote"), Funktion (e) var noteId = $ (this) .data ("noteid"); displayNote (noteId););

Sie finden diese Demo in dem Ordner fulldemo5.


Fazit

Ich hoffe aufrichtig, dass diese Serie für Sie hilfreich war. Wie ich anfangs gesagt habe, war IndexedDB keine Technologie, die ich gerne benutzte. Je mehr ich damit arbeitete und je mehr ich mich damit beschäftigte, wie es funktioniert, desto mehr wurde mir bewusst, wie sehr diese Technologie uns als Webentwickler helfen kann. Es hat definitiv Platz zum Wachsen, und ich kann definitiv sehen, dass Leute es vorziehen, Wrapper-Bibliotheken zu verwenden, um die Dinge zu vereinfachen, aber ich denke, die Zukunft für diese Funktion ist großartig!