Arbeide med IndexedDB - Del 3

Velkommen til endelig en del av IndexedDB-serien. Da jeg begynte denne serien var min hensikt å forklare en teknologi som ikke alltid er den mest vennlige ... å jobbe med. Faktisk, da jeg først prøvde å jobbe med IndexedDB, i fjor, var min første reaksjon noe negativ ("Noe negativ", som om universet er "litt gammelt".). Det har vært en lang reise, men jeg føler meg litt komfortabel med IndexedDB, og jeg respekterer hva det tillater. Det er fortsatt en teknologi som ikke kan brukes overalt (det er dessverre savnet å bli lagt til iOS7), men jeg tror virkelig det er en teknologi folk kan lære og gjøre bruk av i dag.

I denne siste artikkelen skal vi demonstrere noen andre konsepter som bygger på den "fulle" demoen vi bygde i den siste artikkelen. Å være klar, du bli fanget opp på serien eller denne oppføringen vil være vanskelig å følge, så du kan også sjekke ut del ett.


Telle data

La oss starte med noe enkelt. Tenk deg at du vil legge til personsøking til dine data. Hvordan vil du få en telling av dataene dine, slik at du kan håndtere den aktuelle funksjonen? Jeg har allerede vist deg hvordan du kan få alle dataene dine og sikkert du kan bruke det som en måte å telle data på, men det krever at du henter alt. Hvis din lokale database er stor, kan det være sakte. Heldigvis gir IndexedDB-spesifikasjonen en mye enklere måte å gjøre det på.

Tellen () -metoden, som kjøres på et objektStore, returnerer en telling av data. Som alt annet vi har gjort, vil dette være asynkront, men du kan forenkle koden til en samtale. For vår notatdatabase har jeg skrevet en funksjon som heter doCount () det gjør bare dette:

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

Husk - hvis koden ovenfor er litt vanskelig å følge, kan du slå den opp i flere blokker. Se tidligere artikler der jeg demonstrerte dette. Resultatbehandleren er bestått en resultatverdi som representerer det totale antall objekter som er tilgjengelige i butikken. Jeg endret brukergrensesnittet til vår demo for å inkludere et tomt spekter i overskriften.

Notatdatabase 

Den endelige tingen jeg trenger å gjøre er å bare legge til et anrop til doCount når programmet starter og etter noen tillegg eller sletting. Her er et eksempel fra suksesshåndtereren for å åpne databasen.

openRequest.onsuccess = funksjon (e) db = e.target.result; db.onerror = function (event) // Generisk feilhåndterer for alle feilene som er målrettet mot denne databasens // forespørsler! varsling ("Database error:" + event.target.errorCode); ; displayNotes (); doCount (); ;

Du kan finne hele eksemplet i zip du lastet ned som fulldemo2. (Som en FYI, fulldemo1 er programmet som det var på slutten av forrige artikkel.)


Filter som du skriver

For vår neste funksjon, skal vi legge til et grunnleggende filter i notatlisten. I de tidligere artiklene i denne serien dekket jeg hvordan IndexedDB gjør ikke tillate gratis skjema søk. Du kan ikke (vel, ikke lett) søke innhold som inneholder et søkeord. Men med kraften i intervaller, er det lett å støtte støtte i begynnelsen av en streng.

Hvis du husker, lar en rekkevidde oss få tak i data fra en butikk som enten begynner med en bestemt verdi, ender med en verdi eller ligger i mellom. Vi kan bruke dette til å implementere et grunnleggende filter mot tittelen på våre notatfelt. Først må vi legge til en indeks for denne eiendommen. Husk at dette kun kan gjøres i den upupgradeneeded hendelsen.

 hvis (! thisDb.objectStoreNames.contains ("notat")) console.log ("Jeg må lage notatobjektet"); objectStore = thisDb.createObjectStore ("notat", keyPath: "id", autoIncrement: true); objectStore.createIndex ("title", "title", unikt: false); 

Deretter la jeg til et enkelt skjemafelt til brukergrensesnittet:


Deretter la jeg til en "keyup" handler til feltet, så jeg ville se umiddelbare oppdateringer mens jeg skriver.

$ ("# filterField"). På ("tastatur", funksjon (e) var filter = $ (dette) .val (); displayNotes (filter););

Legg merke til hvordan jeg ringer displaynoter. Dette er den samme funksjonen jeg brukte før for å vise alt. Jeg skal oppdatere den for å støtte både "få alt" handling og en "få filtrert" type handling. La oss ta en titt på det.

funksjon displayNotes (filter) var transaksjon = db.transaction (["note"], "readonly"); var content = ""; transaction.oncomplete = funksjon (event) $ (" # notatliste "). html (innhold);; var handleResult = funksjon (hendelse) var markør = event.target.result; = ""; innhold + =""; innhold + =""; innhold + =""; cursor.continue (); else content + ="
Titteloppdatert&
"+ Cursor.value.title +""+ DtFormat (cursor.value.updated) +"Rediger Slett
";; var objectStore = transaction.objectStore (" notat "); hvis (filter) // Kreditt: http://stackoverflow.com/a/8961462/52160 var range = IDBKeyRange.bound (filter, filter + "\ uffff"); var index = objectStore.index ("title"); index.openCursor (rekkevidde) .onsuccess = handleResult; else objectStore.openCursor (). onsuccess = handleResult;

For å være klar er den eneste endringen her nederst. Å åpne en markør med eller uten et utvalg gir oss samme type hendelseshåndteringsresultat. Det er praktisk da det gjør denne oppdateringen så trivial. Det eneste komplekse aspektet er å faktisk bygge rekkevidden. Legg merke til hva jeg har gjort her. Inndata, filter, er hva brukeren skrev inn. Så forestill deg at dette er "The". Vi ønsker å finne notater med en tittel som begynner med "The" og slutter i et hvilket som helst tegn. Dette kan gjøres ved ganske enkelt å sette den fjerne enden av området til et høyt ASCII-tegn. Jeg kan ikke ta æren for denne ideen. Se StackOverflow-lenken i koden for tilskrivning.

Du kan finne denne demoen i fulldemo3 mappe. Merk at dette bruker en ny database, så hvis du har kjørt forrige eksempler, vil denne være tom når du først kjører den.

Mens dette virker, har det et lite problem. Tenk deg et notat med tittelen "Hellig Regel". (Fordi de gjør det. Bare si.) Sannsynligvis vil du prøve å søke etter dette ved å skrive «hellige». Hvis du gjør dette, vil filteret ikke fungere fordi det er saksfølsomt. Hvordan kommer vi rundt det?

En måte er å bare lagre en kopi av tittelen vår i små bokstaver. Dette er relativt enkelt å gjøre. Først endret jeg indeksen for å bruke en ny eiendom som ble kalt titlelc.

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

Da endret jeg koden som lagrer notater for å lage en kopi av feltet:

$ ("# saveNoteButton") på ("klikk", funksjon () var title = $ ("# title") .val (); var body = $ ("# body"). = $ ("# key") .val (); var titlelc = title.toLowerCase (); var t = db.transaction (["notat"], "readwrite"); t.objectStore ("notat") .add (title: title, body: body, oppdatert: ny Dato (), titlelc: titlelc); else t.objectStore ("note") .put tittel, kropp: kropp, oppdatert: ny dato (), id: nummer (nøkkel), titlelc: titlelc);

Endelig endret jeg søket til bare små bokstaver av brukerinngang. På den måten, hvis du går inn i "Hellige", vil det fungere like bra som å skrive inn "hellige".

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

Det er det. Du kan finne denne versjonen som fulldemo4.


Arbeide med Array Properties

For vår siste forbedring skal jeg legge til en ny funksjon i vår Notat-applikasjon - tagging. Dette vil
la deg legge til et hvilket som helst antall koder (tenk nøkkelord som beskriver notatet) slik at du senere kan finne andre
notater med samme tag. Merkene lagres som en matrise. Det er i seg selv ikke så stor avtale. Jeg nevnte i begynnelsen av denne serien at du enkelt kunne lagre arrays som egenskaper. Hva er litt mer komplekst, er å håndtere søket. La oss begynne med å gjøre det slik at du kan legge til koder i et notat.

Først endret jeg notatskjemaet mitt for å få et nytt inntastingsfelt. Dette vil tillate brukeren å skrive inn koder adskilt med et komma:


Jeg kan lagre dette ved å bare oppdatere koden min som håndterer Notatoppretting / oppdatering.

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

Legg merke til at jeg standardiserer verdien til en tom rekkefølge. Jeg bare fylle den hvis du skrev noe inn. Lagring av dette er så enkelt som å legge det til objektet vi overfører til IndexedDB:

 hvis (nøkkel === "") t.objectStore ("notat") .add (tittel: tittel, kropp: kropp, oppdatert: ny dato (), titlelc: titlelc, tags: tags);  andre t.objectStore ("notat") .put (tittel: tittel, kropp: kropp, oppdatert: ny dato (), id: tall (nøkkel), titlelc: titlelc, tags: tags); 

Det er det. Hvis du skriver noen notater og åpner Chrome-fanen Ressurser, kan du faktisk se at dataene blir lagret.


La oss nå legge til koder i visningen når du viser et notat. For min søknad bestemte jeg meg for en enkel brukstilfelle for dette. Når et notat vises, hvis det er tags, vil jeg liste dem ut. Hver tag vil være en lenke. Hvis du klikker på denne linken, viser jeg deg en liste over relaterte notater med samme tag. La oss se på den logikken først.

funksjon displayNote (id) var transaksjon = db.transaction (["notat"]); var objectStore = transaction.objectStore ("notat"); var request = objectStore.get (id); request.onsuccess = funksjon (hendelse) var note = request.result; var content = "

"+ note.title +"

"; hvis (note.tags.length> 0) content + ="Tags: "; note.tags.forEach (funksjon (elm, idx, arr) content + =" "+ elm +" ";); innhold + ="
"; innhold + ="

"+ note.body +"

"; Jeg $ noteDetail.html (innhold) .show (); $ noteForm.hide ();;

Denne funksjonen (et nytt tillegg til vår søknad) håndterer notatskjermkoden formelt bundet til tabellcelleklikkhendelsen. Jeg trengte en mer abstrakt versjon av koden slik at dette oppfyller den hensikten. For det meste er det det samme, men merk logikken for å sjekke lengden på egenskapene for tagger. Hvis arrayet ikke er tomt, er innholdet oppdatert for å inkludere en enkel liste over tagger. Hver enkelt er pakket inn i en lenke med en bestemt klasse jeg vil bruke til oppslag senere. Jeg har også lagt til en div spesifikt for å håndtere det søket.


På dette tidspunktet har jeg muligheten til å legge til koder i et notat så vel som vise dem senere. Jeg har også planlagt å la brukeren klikke på disse kodene, slik at de kan finne andre notater med samme tag. Nå kommer den komplekse delen.

Du har sett hvordan du kan hente innhold basert på en indeks. Men hvordan fungerer det med array egenskaper? Vises ut - spesifikasjonen har et bestemt flagg for å håndtere dette: multiEntry. Når du oppretter en array-basert indeks, må du sette denne verdien til ekte. Slik håndterer programmet mitt det:

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

Det håndterer lagringsaspektet godt. La oss nå snakke om søk. Her er klikkbehandleren for taglink-klassen:

$ (dokument) .on ("klikk", ".tagLookup", funksjon (e) var tag = e.target.text; var parentNote = $ (dette) .data ("noteid"); var doneOne = false; var content = "Relaterte notater:
"; var transaksjon = db.transaction ([" notat "]," readonly "); var objectStore = transaction.objectStore (" notat "); var tagIndex = objectStore.index (" tags "); varavstand = IDBKeyRange.only (tag); transaction.oncomplete = funksjon (event) if (! doneOne) content + = "Ingen andre notater brukt denne taggen."; content + = "

"; $ (" # relatedNotesDisplay ") .html (innhold);; var handleResult = funksjon (hendelse) var markør = event.target.result; if (cursor) if (cursor.value.id! = parentNote) doneOne = true; content + = ""+ cursor.value.title +"
"; cursor.continue ();; tagIndex.openCursor (rekkevidde) .onsuccess = handleResult;);

Det er ganske mye her - men ærlig - det er veldig lik det vi har dicussed før. Når du klikker på en tag, begynner koden min ved å ta tak i teksten til lenken for tagverdien. Jeg lager min transaksjon, objektbutikk og indeksobjekter som du tidligere har sett. Utvalget er nytt denne gangen. I stedet for å skape et område fra noe til noe, kan vi bruke den eneste () api til å spesifisere at vi vil ha et utvalg av bare én verdi. Og ja det virket også rart. Men det fungerer bra. Du kan se da åpner vi markøren, og vi kan deterere over resultatene som før. Det er litt ekstra kode for å håndtere saker der det ikke kan være noen treff. Jeg legger også merke til opprinnelig notat, det vil si den du ser nå, slik at jeg ikke viser det også. Og det er egentlig det. Jeg har en siste bit kode som håndterer klikkhendelser på de relaterte notatene, slik at du enkelt kan se dem:

$ (dokument) .on ("klikk", ".loadNote", funksjon (e) var noteId = $ (dette) .data ("noteid"); displayNote (noteId););

Du kan finne denne demoen i mappen fulldemo5.


Konklusjon

Jeg håper virkelig at denne serien har vært nyttig for deg. Som jeg sa i begynnelsen, var IndexedDB ikke en teknologi jeg likte å bruke. Jo mer jeg jobbet med det, og jo mer jeg begynte å vikle hodet mitt rundt hvordan det gjorde ting, desto mer begynte jeg å sette pris på hvor mye denne teknologien kan hjelpe oss som webutviklere. Det har definitivt plass til å vokse, og jeg kan definitivt se folk foretrekker å bruke wrapper-biblioteker for å forenkle ting, men jeg tror fremtiden for denne funksjonen er flott!