Lucrul cu IndexedDB - Partea 3

Bine ai venit la final parte din seria mea IndexedDB. Când am început această serie, intenția mea era să explic o tehnologie care nu este întotdeauna cea mai prietenoasă cu care să lucrezi. De fapt, când am încercat pentru prima dată să lucrez cu IndexedDB, anul trecut, reacția mea inițială a fost oarecum negativă ("Oarecum negativă" la fel cum Universul este "ceva mai vechi"). A fost o lunga calatorie, dar in cele din urma ma simt mai bine sa lucrez cu IndexedDB si respect ceea ce permite. Este totuși o tehnologie care nu poate fi folosită pretutindeni (din păcate nu a fost adăugată la iOS7), dar cred cu adevărat că este o tehnologie pe care oamenii o pot învăța și de a folosi astăzi.

În acest articol final, vom prezenta câteva concepte suplimentare care se bazează pe demo-ul "complet" pe care l-am construit în ultimul articol. Pentru a fi clar, voi trebuie sa să fie prins pe serie sau această intrare va fi dificil de urmat, deci, poate doriți, de asemenea, pentru a verifica o parte.


Numărătoare de date

Să începem cu ceva simplu. Imaginați-vă că doriți să adăugați paginări la datele dvs. Cum ați obține un număr de date, astfel încât să puteți gestiona corect această caracteristică? V-am arătat deja cum puteți obține toate datele dvs. și, cu siguranță, ați putea să o utilizați ca pe o modalitate de a număra date, dar acest lucru necesită preluarea tuturor. Dacă baza de date locală este imensă, ar putea fi lentă. Din fericire, spec. IndexedDB oferă o modalitate mult mai simplă de a face acest lucru.

Metoda count (), executată pe un objectStore, va returna un număr de date. Ca orice altceva pe care l-am făcut, va fi asincron, dar puteți simplifica codul la un singur apel. Pentru baza noastră de date, am scris o funcție numită doCount () care face acest lucru:

funcția () () () () () () () ("(")) "+ event.target.result +" Note Total "); ; 

Rețineți - dacă codul de mai sus este puțin greu de urmat, îl puteți descompune în mai multe blocuri. Vedeți articolele anterioare în care am demonstrat acest lucru. Managerul de rezultate este trecut printr-o valoare a rezultatului reprezentând numărul total de obiecte disponibile în magazin. Am modificat interfața de utilizare a demo-ului nostru pentru a include un spațiu gol în antet.

Notă Bază de date 

Ultimul lucru pe care trebuie să-l fac este să adaug un simplu apel la doCount când aplicația pornește și după orice operație de adăugare sau ștergere. Iată un exemplu de la managerul de succes pentru deschiderea bazei de date.

openRequest.onsuccess = funcția (e) db = e.target.result; db.onerror = functie (eveniment) // Generator de erori generic pentru toate erorile directionate la // cererile acestei baze de date! alertă ("Eroare bază de date:" + event.target.errorCode); ; displayNotes (); doCount (); ;

Puteți găsi exemplul complet în zip-ul pe care l-ați descărcat ca fulldemo2. (Ca FYI, fulldemo1 este cererea așa cum a fost la sfârșitul articolului precedent.)


Filtrați în timp ce tastați

Pentru următoarea noastră caracteristică, vom adăuga un filtru de bază în lista de note. În articolele anterioare din această serie am abordat modul în care face IndexedDB nu permiteți căutarea gratuită a formularului. Nu puteți căuta (nu, ușor) căutarea de conținut conține un cuvânt cheie. Dar, cu puterea intervalelor, este ușor să se sprijine cel puțin la începutul unui șir.

Dacă vă aduceți aminte, o gamă ne permite să luăm datele dintr-un magazin care începe fie cu o anumită valoare, fie cu o valoare, fie cu o valoare între. Putem folosi aceasta pentru a implementa un filtru de bază împotriva titlului câmpurilor noastre de notă. În primul rând, trebuie să adăugăm un index pentru această proprietate. Amintiți-vă, acest lucru se poate face doar în cazul evenimentului onupgradeneeded.

 dacă (! thisDb.objectStoreNames.contains ("notă")) console.log ("trebuie să fac nota obiectelor"); objectStore = thisDb.createObjectStore ("notă", keyPath: "id", autoIncrement: true); objectStore.createIndex ("titlu", "titlu", unic: fals); 

Apoi, am adăugat un câmp de formular simplu la UI:


Apoi am adăugat un "handler" la câmp, pentru a vedea actualizări imediate în timp ce tastez.

$ ("# filterField") pe ("keyup", funcția (e) var filter = $ (this) .val (); displayNotes (filter););

Observați cum numesc displayNotes. Aceasta este aceeași funcție pe care am folosit-o înainte pentru a afișa totul. Am de gând să-l actualizez pentru a sprijini atât acțiunea "a lua totul", cât și acțiunea de tip "get filtered". Să aruncăm o privire la ea.

Afișați funcțiaNote (filtru) var transaction = db.transaction (["notă"], "readonly"); var content = ""(cursor) content ()) $ (" # noteList ") html (content); var handleResult = = ""; conținut + =""; conținut + =""; conținut + =""; cursor.continue (); altceva content + ="
TitluLa curent&
"+ Cursor.value.title +""+ DtFormat (cursor.value.updated) +"Editați Ștergeți
"() () () () () () () () filtru + filtru + "\ uffff"); var index = ObjectStore.index ("title"); index.openCursor (interval) .insuccess = handleResult; altceva objectStore.openCursor () onsuccess = handleResult;

Pentru a fi clar, singura schimbare aici este în partea de jos. Deschiderea unui cursor cu sau fără un interval ne dă același tip de rezultat al manipulării evenimentului. Acest lucru este util atunci când face ca această actualizare să fie atât de banală. Singurul aspect complex este acela de a construi efectiv gama. Observați ce am făcut aici. Intrarea, filtrul, este ceea ce a tastat utilizatorul. Imaginați-vă că acesta este "The". Vrem să găsim note cu un titlu care începe cu "The" și se termină cu orice caracter. Acest lucru se poate face prin simpla setare a capătului îndepărtat al intervalului la un caracter ASCII ridicat. Nu pot să cred în această idee. Vedeți linkul StackOverflow din codul de atribuire.

Puteți găsi această demonstrație în fulldemo3 pliant. Rețineți că aceasta utilizează o nouă bază de date, astfel încât dacă ați rulat exemplele anterioare, aceasta va fi goală când o rulați pentru prima oară.

În timp ce funcționează, are o mică problemă. Imaginați-vă o notă intitulată "Regula Sfinților". (Pentru că o fac, doar spunând.) Cel mai probabil veți încerca să căutați acest lucru prin tastarea "sfinților". Dacă faceți acest lucru, filtrul nu va funcționa deoarece este sensibil la minuscule. Cum ajungem în jur?

O modalitate este să stoca pur și simplu o copie a titlului nostru în litere mici. Acest lucru este relativ ușor de făcut. În primul rând, am modificat indexul pentru a folosi o nouă proprietate numită titlelc.

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

Apoi am modificat codul care stochează note pentru a crea o copie a câmpului:

$ ("# saveNoteButton") pe ("click", funcția () var title = $ ("# title"). = ("cheie"); val (); var titlulc = title.toLowerCase (); var t = db.transaction (["notă" t.objectStore ("notă") .add (title: title, body: corp, actualizat: new Date (), titlelc: titlelc titlu, corp: corp, actualizat: nou Data (), id: Număr (cheie), titlulc: titlulc);

În cele din urmă, am modificat căutarea doar pentru introducerea în câmpul de utilizator. În felul acesta, dacă intrați în "Sfinți", aceasta va funcționa la fel de bine ca și intrarea "sfinților".

 filter = filter.toLowerCase (); var domeniu = IDBKeyRange.bound (filtru, filtru + "\ uffff"); var index = ObiectStore.index ("titlelc");

Asta e. Puteți găsi această versiune ca fulldemo4.


Lucrul cu proprietățile matricei

Pentru îmbunătățirea noastră finală, voi adăuga o nouă caracteristică în etichetarea aplicației Notificare. Asta va
permiteți adăugarea oricărui număr de etichete (gândiți cuvintele cheie care descriu nota), astfel încât să puteți găsi mai târziu altele
note cu aceeași etichetă. Etichetele vor fi stocate ca o matrice. Aceasta însăși nu este o afacere atât de mare. Am menționat la începutul acestei serii că ați putea depozita cu ușurință array-urile ca proprietăți. Ceea ce este puțin mai complex este gestionarea căutării. Să începem prin a face astfel încât să puteți adăuga etichete la o notă.

În primul rând, am modificat formularul de notă pentru a avea un câmp de intrare nou. Acest lucru va permite utilizatorului să introducă etichete separate printr-o virgulă:


Pot salva acest lucru prin actualizarea pur și simplu a codului meu care gestionează crearea / actualizarea notelor.

 var tags = []; var tagString = $ ("# tag-uri") val (); dacă (tagString.length) tags = tagString.split (",");

Observați că am defectat valoarea la o matrice goală. Am o populare numai dacă ați introdus ceva. Salvarea acestui lucru este la fel de simplă ca adăugarea lui la obiectul pe care îl trecem la IndexedDB:

 dacă key === "") t.objectStore ("notă") .add (title: title, body: corp, actualizat: new Date (), titlelc: titlelc, tags: tags);  altceva t.objectStore ("notă") .put (title: title, body: body, actualizat: new Date (), id: number (key), titlelc: titlelc, tags: tags); 

Asta e. Dacă scrieți câteva note și deschideți fila Resurse Chrome, puteți vedea efectiv datele stocate.


Acum, să adăugăm etichete în vizualizare atunci când afișezi o notă. Pentru cererea mea, am decis un caz simplu de utilizare pentru aceasta. Când se afișează o notă, dacă există etichete, le voi lista. Fiecare etichetă va fi o legătură. Dacă dați clic pe acel link, vă vom arăta o listă de note asociate utilizând aceeași etichetă. Să ne uităm mai întâi la această logică.

funcția displayNote (id) var transaction = db.transaction (["notă"]); var objectStore = transaction.objectStore ("notă"); var cerere = objectStore.get (id); request.onsuccess = funcție (eveniment) var note = request.result; var content = "

"+ note.title +"

"dacă (note.tags.length> 0) content + ="Etichete: "; note.tags.forFiecare (functie (elm, idx, arr) content + =" "+ elm +" "
"; conținut + ="

"+ note.body +"

"; $ NoteDetail.html (conținut) .show (); $ noteForm.hide ();;

Această funcție (o nouă adăugire a aplicației noastre) se ocupă de codul de afișare a notei, legat în mod formal de evenimentul de clicuri de celule din tabel. Aveam nevoie de o versiune mai abstractă a codului, astfel încât acest scop să fie îndeplinit. În cea mai mare parte este același lucru, dar rețineți logica pentru a verifica lungimea proprietăților etichetelor. Dacă matricea nu este goală, conținutul este actualizat pentru a include o listă simplă de etichete. Fiecare dintre ele este înfășurat într-o legătură cu o anumită clasă pe care o voi folosi pentru căutare mai târziu. De asemenea, am adăugat un div special pentru a face față acestei căutări.


În acest moment, am posibilitatea de a adăuga etichete la o notă, precum și să le afișeze mai târziu. De asemenea, am planificat să permitem utilizatorului să facă clic pe acele etichete, astfel încât să poată găsi alte note utilizând aceeași etichetă. Acum vine partea complexă.

Ați văzut cum puteți prelua conținutul pe baza unui indice. Dar cum funcționează aceasta cu proprietățile matricei? Se pare - spec. Are un steag specific pentru a trata acest lucru: multiEntry. Când creați un index bazat pe matrice, trebuie să setați această valoare la true. Iată cum gestionează aplicația mea:

objectStore.createIndex ("etichete", "etichete", unic: false, multiEntry: true);

Care manipulează bine aspectul de stocare. Acum să vorbim despre căutare. Iată manualul de clic pentru clasa de link-uri pentru etichete:

$ (document) .on ("click", ".tagLookup", functie (e) var tag = e.target.text; var parentNote = $ (aceasta) .data ("noteid"); var doneOne = false; var content = "Note înrudite:
"var objectStore = transaction.objectStore (" notă "); var tagIndex = objectStore.index (" tags "); var range = IDBKeyRange.only (tag); transaction.oncomplete = functie (eveniment) if (! doneOne) content + = "Nici o alta nota nu a folosit aceasta eticheta.

"(cursor) if (cursor.value.id! = parentNote)"; $ ("# relatedNotesDisplay" doneOne = true; content + = ""+ cursor.value.title +"
"; cursor.continue ();; tagIndex.openCursor (interval) .insuccess = handleResult;);

Sunt destul de puțin aici - dar sincer - este foarte asemănător cu ceea ce am discutat înainte. Când faceți clic pe o etichetă, codul meu începe prin apucarea textului linkului pentru valoarea etichetei. Îmi creez obiectele de tranzacție, obiecte de obiecte și obiecte indexate așa cum ați văzut înainte. Intervalul este nou de data aceasta. În loc să creăm o gamă de ceva și ceva, putem folosi numai () api pentru a specifica că dorim o gamă de o singură valoare. Și da - mi sa părut ciudat. Dar funcționează minunat. Puteți vedea atunci deschidem cursorul și putem itera asupra rezultatelor ca mai înainte. Există un pic de cod suplimentar pentru a trata cazurile în care nu pot exista meciuri. De asemenea, iau notă de original notă, adică cea pe care o vizualizați acum, astfel încât să nu o afișez și ea. Și asta este cu adevărat. Am un ultim cod de cod care gestionează evenimentele pe clic pe acele note legate, astfel încât să le puteți vedea cu ușurință:

$ (document) .on ("faceți clic pe", ".loadNote", funcția (e) var noteId = $ (this) .data ("noteid"); displayNote (noteId);

Puteți găsi această demonstrație în dosar fulldemo5.


Concluzie

Sper sincer că această serie a fost de ajutor pentru dvs. Așa cum am spus la început, IndexedDB nu era o tehnologie pe care mi-a plăcut să o folosesc. Cu cât am lucrat mai mult cu el și cu cît am început să-mi înfășez capul în jurul valorii de lucruri, cu atât mai mult am început să apreciez cât de mult această tehnologie ne-ar putea ajuta ca dezvoltatori web. Cu siguranta are loc sa se dezvolte si pot vedea cu siguranta ca oamenii prefera sa foloseasca bibliotecile de tip wrapper pentru a simplifica lucrurile, dar cred ca viitorul acestei caracteristici este minunat!

Cod