In het eerste deel van deze miniserie hebben we de basisstructuur van een taak gemaakt met een Sinatra JSON-interface naar een SQLite-database en een front-end met knock-out waarmee we taken aan onze database kunnen toevoegen. In dit laatste deel behandelen we iets meer geavanceerde functionaliteit in Knockout, inclusief sorteren, zoeken, bijwerken en verwijderen.
Laten we beginnen waar we waren gebleven; hier is het relevante gedeelte van onze index.erb
het dossier.
Maak een nieuwe taak
Zoektaken
Nog niet voltooide taken:
Verwijder alle voltooide taken
DB ID Omschrijving Datum toegevoegd Datum gewijzigd Compleet? Verwijder X
Sorteren is een veelvoorkomende taak die in veel toepassingen wordt gebruikt. In ons geval willen we de takenlijst sorteren op elk veld in onze takenlijst. We beginnen met het toevoegen van de volgende code aan de TaskViewModel
:
t.gesorteerdBy = []; t.sort = function (field) if (t.sortedBy.length && t.sortedBy [0] == field && t.sortedBy [1] == 1) t.sortedBy [1] = 0; t.tasks.sort (functie (eerst, volgende) if (! volgende [veld]. call ()) return 1; return (volgende [veld] .call () < first[field].call()) ? 1 : (next[field].call() == first[field].call()) ? 0 : -1; ); else t.sortedBy[0] = field; t.sortedBy[1] = 1; t.tasks.sort(function(first,next) if (!first[field].call()) return 1; return (first[field].call() < next[field].call()) ? 1 : (first[field].call() == next[field].call()) ? 0 : -1; );
Knockout biedt een sorteerfunctie voor waarneembare matrices
Eerst definiëren we een sortedBy
array als een eigenschap van ons kijkmodel. Hiermee kunnen we opslaan of en hoe de verzameling is gesorteerd.
Het volgende is het soort()
functie. Het accepteert a veld-
argument (het veld dat we willen sorteren op) en controleert of de taken zijn gesorteerd op basis van het huidige sorteringsschema. We willen sorteren met behulp van een "schakel" type proces. Sorteer bijvoorbeeld één keer op omschrijving en de taken worden op alfabetische volgorde gerangschikt. Sorteer opnieuw op omschrijving en de taken schikken in omgekeerde alfabetische volgorde. Deze soort()
functie ondersteunt dit gedrag door het meest recente sorteerschema te controleren en te vergelijken met wat de gebruiker wil sorteren.
Knockout biedt een sorteerfunctie voor waarneembare matrices. Het accepteert een functie als een argument dat bepaalt hoe de array moet worden gesorteerd. Deze functie vergelijkt twee elementen uit de array en retourneert 1
, 0
, of -1
als resultaat van die vergelijking. Alle dezelfde waarden zijn gegroepeerd (wat handig zal zijn om complete en onvolledige taken samen te groeperen).
Opmerking: de eigenschappen van de array-elementen moeten worden opgeroepen in plaats van eenvoudig worden geopend; deze eigenschappen zijn eigenlijk functies die de waarde van de eigenschap retourneren als deze zonder argumenten wordt aangeroepen.
Vervolgens definiëren we de bindingen op de tabelkoppen in onze mening.
DB ID Omschrijving Datum toegevoegd Datum gewijzigd Compleet? Verwijder
Met deze bindingen kan elk van de headers een sortering activeren op basis van de doorgegeven tekenreekswaarde; elk van deze kaarten is direct gekoppeld aan de Taak
model-.
Vervolgens willen we een taak als voltooid kunnen markeren en we zullen dit doen door eenvoudigweg op het selectievakje voor een specifieke taak te klikken. Laten we beginnen met het definiëren van een methode in de TaskViewModel
:
t.markAsComplete = function (task) if (task.complete () == true) task.complete (true); else task.complete (false); task._method = "put"; t.saveTask (taak); geef waar terug;
De markAsComplete ()
methode accepteert de taak als een argument, dat automatisch wordt doorgegeven door Knockout bij het itereren over een verzameling items. We schakelen vervolgens de compleet
eigendom, en voeg een toe ._method = "put"
eigendom van de taak. Dit staat toe DataMapper
om de HTTP te gebruiken LEGGEN
werkwoord in tegenstelling tot POST
. We gebruiken dan onze handige t.saveTask ()
methode om de wijzigingen in de database op te slaan. Eindelijk keren we terug waar
omdat ze terugkeren vals
voorkomt dat het selectievakje van status verandert.
Vervolgens veranderen we de weergave door het selectievakje in de taakkring te vervangen door het volgende:
Dit vertelt ons twee dingen:
compleet
is waar.markAsComplete ()
functie van de ouder (TaskViewModel
in dit geval). Hierdoor wordt de huidige taak automatisch in de lus doorgegeven. Om een taak te verwijderen, gebruiken we eenvoudigweg een paar gemaksmethoden en bellen saveTask ()
. In onze TaskViewModel
, voeg het volgende toe:
t.destroyTask = function (task) task._method = "delete"; t.tasks.destroy (taak); t.saveTask (taak); ;
Met deze functie wordt een eigenschap toegevoegd die lijkt op de "put" -methode voor het voltooien van een taak. De ingebouwde vernietigen()
methode verwijdert de doorgegeven taak uit de waarneembare array. Eindelijk bellen saveTask ()
vernietigt de taak; dat is, zolang het ._methode
is ingesteld op "verwijderen".
Nu moeten we onze mening wijzigen; voeg het volgende toe:
X
Dit komt qua functionaliteit overeen met het complete selectievakje. Merk op dat de class = "destroytask"
is puur voor stylingdoeleinden.
Vervolgens willen we de functie "Alle volledige taken verwijderen" toevoegen. Voeg eerst de volgende code toe aan de TaskViewModel
:
t.removeAllComplete = function () ko.utils.arrayForEach (t.tasks (), function (task) if (task.complete ()) t.destroyTask (task););
Deze functie herhaalt eenvoudigweg de taken om te bepalen welke ervan compleet zijn en we noemen het destroyTask ()
methode voor elke volledige taak. Voeg in onze optiek het volgende toe voor de link "verwijder alles".
0 "> Verwijder alle voltooide taken
Onze klikbinding werkt correct, maar we moeten definiëren completeTasks ()
. Voeg het volgende toe aan onze TaskViewModel
:
t.completeTasks = ko.computed (function () return ko.utils.arrayFilter (t.tasks (), function (task) return (task.complete () && task._method! = "delete")); );
Deze methode is een berekende eigendom. Deze eigenschappen retourneren een waarde die 'on the fly' wordt berekend wanneer het model wordt bijgewerkt. In dit geval retourneren we een gefilterde array die alleen volledige taken bevat die niet zijn gemarkeerd voor verwijdering. Vervolgens gebruiken we deze array's gewoon lengte
eigenschap om de link "Verwijder alle voltooide taken" te verbergen of weer te geven.
Onze interface moet ook de hoeveelheid onvolledige taken weergeven. Gelijk aan onze completeTasks ()
functie hierboven, definiëren we een incompleteTasks ()
functie in TaskViewModel
:
t.incompleteTasks = ko.computed (function () return ko.utils.arrayFilter (t.tasks (), function (task) return (! task.complete () && task._method! = "delete")) ;);
We hebben dan toegang tot deze berekende gefilterde array in onze mening, als volgt:
Nog niet voltooide taken:
We willen de voltooide items anders indelen dan de taken in de lijst, en we kunnen dit in onze ogen doen met Knockout's css
verbindend. Wijzig de tr
openingstag in onze taak arrayForEach ()
loop naar het volgende.
Dit voegt een toe
compleet
CSS-klasse voor de tabelrij voor elke taak als dezecompleet
eigendom iswaar
.
Data opschonen
Laten we die lelijke Ruby-datakoorden verwijderen. We beginnen met het definiëren van een
datumnotatie
functie in onzeTaskViewModel
:t.MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", " december "]; t.dateFormat = function (date) if (! date) retourneer "verversen om de serverdatum te zien"; var d = new Datum (datum); return d.getHours () + ":" + d.getMinutes () + "," + d.getDate () + "" + t.MONTHS [d.getMonth ()] + "," + d.getFullYear () ;Deze functie is redelijk eenvoudig. Als de datum om welke reden dan ook niet is gedefinieerd, hoeven we alleen de browser te vernieuwen om de datum in de initiaal in te voeren
Taak
ophaalfunctie. Anders maken we een door de mens leesbare datum met het gewone JavaScriptDatum
object met behulp van deMAANDEN
matrix. (Opmerking: het is niet nodig om de naam van de array te kapitaliserenMAANDEN
, natuurlijk; dit is gewoon een manier om te weten dat dit een constante waarde is die niet mag worden veranderd.)Vervolgens voegen we de volgende wijzigingen toe aan onze weergave voor de
gemaakt bij
enupdated_at
eigenschappen:Dit verstrijkt de
gemaakt bij
enupdated_at
eigenschappen van dedatumnotatie()
functie. Nogmaals, het is belangrijk om te onthouden dat eigenschappen van elke taak geen normale eigenschappen zijn; het zijn functies. Om hun waarde op te halen, moet u de functie aanroepen (zoals in het bovenstaande voorbeeld). Notitie:$ wortel
is een sleutelwoord, gedefinieerd door Knockout, dat verwijst naar het ViewModel. Dedatumnotatie()
methode, bijvoorbeeld, is gedefinieerd als een methode van de root ViewModel (TaskViewModel
).
Taken zoeken
We kunnen onze taken op verschillende manieren doorzoeken, maar we houden de dingen eenvoudig en voeren een front-end zoekopdracht uit. Houd er echter rekening mee dat het waarschijnlijk is dat deze zoekresultaten door de database worden gegenereerd terwijl de gegevens groeien omwille van paginering. Maar voor nu, laten we onze definiëren
zoeken()
methode opTaskViewModel
:t.query = ko.observable ("); t.search = function (task) ko.utils.arrayForEach (t.tasks (), function (task) if (task.description () && t.query () ! = "") task.isvisible (task.description (). toLowerCase (). indexOf (t.query (). toLowerCase ())> = 0); else if (t.query () == "" ) task.isvisible (true); else task.isvisible (false);) return true;We kunnen zien dat dit itereert door de reeks taken en controles om te zien of
t.query ()
(een normale waarneembare waarde) staat in de taakomschrijving. Merk op dat deze controle daadwerkelijk in de setter functie voor detask.isvisible
eigendom. Als de evaluatie isvals
, de taak is niet gevonden en deis zichtbaar
eigenschap is ingesteld opvals
. Als de query gelijk is aan een lege tekenreeks, zijn alle taken ingesteld om zichtbaar te zijn. Als de taak geen beschrijving heeft en de query een niet-lege waarde is, maakt de taak geen deel uit van de geretourneerde gegevensset en is deze verborgen.In onze
index.erb
bestand, stellen we onze zoekinterface in met de volgende code:De invoerwaarde is ingesteld op de
ko. waarneembare vraag
. Vervolgens zien we dat hetkeyup
evenement wordt specifiek geïdentificeerd als eenvalueUpdate
evenement. Ten slotte hebben we een handmatige gebeurtenis gebonden aankeyup
om de zoekopdracht uit te voeren (t.search ()
) functie. Er is geen formulierinzending nodig; de lijst met overeenkomende items wordt weergegeven en kan nog steeds worden gesorteerd, verwijderd, enz. Daarom werken alle interacties altijd.
Eindcode
index.erb
Te doen Maak een nieuwe taak
Zoektaken
Nog niet voltooide taken:
0 "> Verwijder alle voltooide taken
DB ID Omschrijving Datum toegevoegd Datum gewijzigd Compleet? Verwijder X app.js
function Task (data) this.description = ko.observable (data.description); this.complete = ko.observable (data.complete); this.created_at = ko.observable (data.created_at); this.updated_at = ko.observable (data.updated_at); this.id = ko.observable (data.id); this.isvisible = ko.observable (true); function TaskViewModel () var t = this; t.tasks = ko.observableArray ([]); t.newTaskDesc = ko.observable (); t.gesorteerdBy = []; t.query = ko.observable ("); t.MONTHS = [" Jan "," Feb "," Mar "," Apr "," May "," Jun "," Jul "," Aug "," Sep "," Oct "," Nov "," Dec "]; $ .getJSON (" http: // localhost: 9393 / tasks ", function (raw) var tasks = $ .map (raw, function (item) retourneer nieuwe taak (item)); t.tasks (taken);); t.incompleteTasks = ko.computed (function () return ko.utils.arrayFilter (t.tasks (), function (task) ga terug (! task.complete () && task._method! = "delete"));); t.completeTasks = ko.computed (function () return ko.utils.arrayFilter (t.tasks (), function ( taak) return (task.complete () && task._method! = "delete"));); // Operations t.dateFormat = function (date) if (! date) return "vernieuwen om server te zien datum "; var d = new Datum (datum); retourneer d.getHours () +": "+ d.getMinutes () +", "+ d.getDate () +" "+ t.MONTHS [d.getMonth ()] + "," + d.getFullYear (); t.addTask = function () var newtask = new Task (description: this.newTaskDesc ()); $ .getJSON ("/ getdate", function (gegevens) newtask.created_at (data.date); newtask.up dated_at (data.date); t.tasks.push (newtask); t.saveTask (newtask); t.newTaskDesc ( ""); ); t.search = function (task) ko.utils.arrayForEach (t.tasks (), function (task) if (task.description () && t.query ()! = "") task.isvisible (taak .description (). toLowerCase (). indexOf (t.query (). toLowerCase ())> = 0); else if (t.query () == "") task.isvisible (true); else task.isvisible (false);) return true; t.sort = function (field) if (t.sortedBy.length && t.sortedBy [0] == field && t.sortedBy [1] == 1) t.sortedBy [1] = 0; t.tasks.sort (functie (eerst, volgende) if (! volgende [veld]. call ()) return 1; return (volgende [veld] .call () < first[field].call()) ? 1 : (next[field].call() == first[field].call()) ? 0 : -1; ); else t.sortedBy[0] = field; t.sortedBy[1] = 1; t.tasks.sort(function(first,next) if (!first[field].call()) return 1; return (first[field].call() < next[field].call()) ? 1 : (first[field].call() == next[field].call()) ? 0 : -1; ); t.markAsComplete = function(task) if (task.complete() == true) task.complete(true); else task.complete(false); task._method = "put"; t.saveTask(task); return true; t.destroyTask = function(task) task._method = "delete"; t.tasks.destroy(task); t.saveTask(task); ; t.removeAllComplete = function() ko.utils.arrayForEach(t.tasks(), function(task) if (task.complete()) t.destroyTask(task); ); t.saveTask = function(task) var t = ko.toJS(task); $.ajax( url: "http://localhost:9393/tasks", type: "POST", data: t ).done(function(data) task.id(data.task.id); ); ko.applyBindings(new TaskViewModel());Let op de herschikking van eigendomsverklaringen op de
TaskViewModel
.
Conclusie
Je hebt nu de technieken om complexere applicaties te maken!
Deze twee tutorials hebben je door het proces geleid van het maken van een applicatie met één pagina met Knockout.js en Sinatra. De applicatie kan gegevens schrijven en ophalen, via een eenvoudige JSON-interface, en heeft functies die verder gaan dan eenvoudige CRUD-acties, zoals massale verwijdering, sorteren en zoeken. Met deze hulpmiddelen en voorbeelden beschikt u nu over de technieken om veel complexere applicaties te maken!