TodoMVC auf dap schreiben. Teil 2

Dies ist der zweite, letzte Teil des Tutorials, in dem wir den TodoMVC-Client unter Verwendung des minimalistischen reaktiven ds js-Frameworks schreiben.

Zusammenfassung des ersten Teils : Wir haben vom Server eine Aufgabenliste im JSON-Format erhalten, daraus eine HTML-Liste erstellt, die Möglichkeit hinzugefĂŒgt, den Namen und das Abschlusszeichen fĂŒr jeden Fall zu bearbeiten, und eine Serverbenachrichtigung ĂŒber diese Änderungen implementiert.

Es bleibt abzuwarten: Löschen beliebiger FĂ€lle, HinzufĂŒgen neuer FĂ€lle, Masseninstallation / ZurĂŒcksetzen und Filtern von FĂ€llen nach Fertigstellung und die Funktion zum Löschen aller abgeschlossenen FĂ€lle. Das werden wir tun. Die endgĂŒltige Version des Clients, auf die wir in diesem Artikel eingehen, finden Sie hier .



Die Option, die wir letztes Mal gewÀhlt haben, kann hier aufgefrischt werden .

Hier ist sein Code:

'#todoapp'.d("" ,'#header'.d("" ,'H1'.d("") ,'INPUT#new-todo placeholder="What needs to be done?" autofocus'.d("") ) ,'#main'.d("" ,'#toggle-all type=checkbox'.d("") ,'UL#todo-list'.d("*@ todos:query" ,'LI'.d("$completed=.completed $editing= $patch=; a!" ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=($completed=#.checked)") ,'LABEL.view' .d("? $editing:!; ! .title") .e("dblclick","$editing=`yes") ,'INPUT.edit' .d("? $editing; !! .title@value") .ui("$patch=(.title=#.value)") .e("blur","$editing=") ,'BUTTON.destroy'.d("") ) .a("!? $completed $editing") .u("? $patch; (@method`PATCH .url:dehttp headers $patch@):query $patch=") ) ) ,'#footer'.d("" ,'#todo-count'.d("") ,'UL#filters'.d("" ,'LI'.d("") ) ,'#clear-completed'.d("") ) ) .DICT({ todos : "//todo-backend-express.herokuapp.com/", headers: {"Content-type":"application/json"} }) .FUNC({ convert:{ dehttp: url=>url.replace(/^https?\:/,'') } }) .RENDER() 


Jetzt gibt es hier nur noch fĂŒnfzig Zeilen, aber am Ende des Artikels werden es doppelt so viele sein - bis zu 100. Es werden viele HTTP-Anfragen an den Server gesendet. Öffnen Sie daher bitte die Entwicklertools (in Chrome, wie Sie sich erinnern, Strg + Umschalt + I) Erstens ist die Registerkarte Netzwerk interessant, und zweitens die Konsole. Vergessen Sie auch nicht, den Code fĂŒr jede Version unserer Seite anzuzeigen - in Chrome ist dies Strg + U.

Hier muss ich einen kleinen Exkurs machen. Wenn Sie den ersten Teil des Tutorials nicht gelesen haben, wĂŒrde ich dennoch empfehlen, damit zu beginnen. Wenn Sie es gelesen haben, aber nichts verstanden haben, ist es besser, es erneut zu lesen. Wie die Kommentare zu meinen beiden vorherigen Artikeln zeigen, werden die Syntax und das Prinzip von dap von einem unvorbereiteten Leser nicht immer sofort verstanden. Ein anderer Artikel ist nicht zum Lesen fĂŒr Personen zu empfehlen, denen das Auftreten einer nicht si-Ă€hnlichen Syntax unangenehm ist.



Dieser zweite Teil des Tutorials ist etwas komplizierter und interessanter als der erste. [TODO: Bitten Sie Token, einen SchĂŒler zu finden, der im Internet ein Gehirnbild explodiert] .

Mit Ihrer Erlaubnis werde ich die Kapitel mit Teil 1 weiter nummerieren. Dort haben wir bis 7 gezÀhlt. Also,

8. Erstellen einer Aufgabenliste fĂŒr die Statusvariable


Um einen Fall aus der Liste zu entfernen, gibt es eine SchaltflÀche BUTTON.destroy . Das Entfernen besteht darin, eine DELETE-Anforderung an den Server zu senden und das entsprechende UL#todo-list > LI Element UL#todo-list > LI mit dem gesamten Inhalt tatsÀchlich aus dem Blickfeld zu entfernen. Mit dem Senden einer DELETE-Anfrage ist alles klar:

  ,'BUTTON.destroy'.ui("(@method`DELETE .url:dehttp):query") 


Mit dem Entfernen eines Elements vom Bildschirm sind jedoch Optionen möglich. Man könnte einfach eine andere Statusvariable einfĂŒhren, beispielsweise $deleted , und das Element mit CSS ausblenden, einschließlich der deleted CSS-Klasse

  ,'LI'.d("$completed=.completed $editing= $patch= $deleted=; a!" //  $deleted   "" ... ,'BUTTON.destroy'.d("(@method`DELETE .url:dehttp):query $deleted=`yes") //  $deleted -     ) .a("!? $completed $editing $deleted") //   CSS  .deleted{display:none} 


Und es wĂŒrde funktionieren. Aber es wĂ€re Betrug. DarĂŒber hinaus werden wir weiter unten Filter und ZĂ€hler fĂŒr aktive und abgeschlossene FĂ€lle haben (was in #footer ). Daher ist es besser, das Objekt sofort „physisch“ von der To-Do-Liste zu entfernen. Das heißt, wir mĂŒssen das Array selbst Ă€ndern können, das wir ursprĂŒnglich vom Server erhalten haben. Das bedeutet, dass dieses Array auch eine Statusvariable werden muss. Nennen wir sie $todos .

Der Bereich der Variablen $todos besteht darin, den gemeinsamen Vorfahren aller Elemente auszuwÀhlen, die auf diese Variable zugreifen. Und es wird durch INPUT#new-todo von #header und ZÀhler von #footer und tatsÀchlich UL#todo-list #footer . Der gemeinsame Vorfahr von allen ist das Wurzelelement der Vorlage, #todoapp . Daher definieren wir in seiner D-Regel die Variable $todos . An derselben Stelle laden wir die Daten sofort vom Server auf den Server hoch. Und um die Liste UL#todo-list zu erstellen, werden wir jetzt auch davon sein:

 '#todoapp'.d("$todos=todos:query" //   $todos      ... ,'UL#todo-list'.d("*@ $todos" //     $todos 


Es ist wichtig. Wenn beim Testen plötzlich die To-Do-Liste nicht geladen wird - ist es gut möglich, dass jemand sie alle gelöscht hat (dies ist ein öffentlicher Server, und dort kann alles passieren).
In diesem Fall gehen Sie bitte zu einem vollstÀndigen Beispiel und erstellen Sie einige FÀlle, mit denen Sie experimentieren können.

Wir schauen Hier wird $todos in der d-Regel des #todoapp Elements #todoapp und sofort mit den erforderlichen Daten initialisiert . Alles scheint zu funktionieren, aber eine unangenehme Eigenschaft ist aufgetreten. Wenn der Server lÀngere Zeit auf die Anfrage reagiert (in Chrome können Sie diese Situation simulieren: Auf der Registerkarte "Netzwerk" der Entwicklertools können Sie verschiedene Modi zum Simulieren langsamer Netzwerke auswÀhlen), sieht unsere neue Version der Anwendung bis zur Fertigstellung der Anfrage ein wenig traurig aus - es gibt nur CSS Artefakte. Ein solches Bild wird den Benutzer definitiv nicht begeistern. Obwohl die VorgÀngerversion nicht darunter litt - bis die Daten auf der Seite eingingen, fehlte nur die Liste selbst, aber andere Elemente tauchten sofort auf, ohne auf die Daten zu warten.

Hier ist das Ding. Wie Sie sich erinnern, ist der :query asynchron. Diese AsynchronitĂ€t drĂŒckt sich darin aus, dass bis zum Abschluss der Anforderung nur die AusfĂŒhrung der aktuellen Regel blockiert ist, dh die Generierung des Elements, das die angeforderten Daten tatsĂ€chlich benötigt (was logisch ist). Die Erzeugung anderer Elemente wird nicht blockiert. Daher wurde beim Zugriff von UL#todo-list auf den Server nur dieser blockiert, nicht jedoch #header und #footer , die sofort gezeichnet wurden. Nun wartet die gesamte #todoapp auf die Fertigstellung der Anfrage.

9. Verzögertes Laden der Daten


Um die Situation zu korrigieren und nicht beteiligte Elemente nicht zu blockieren, verschieben wir das erstmalige Laden der Daten, bis alles bereits gezeichnet ist. Dazu laden wir die Daten nicht sofort in die Variable $todos , sondern initialisieren sie zunĂ€chst einfach mit „nichts“.

 '#todoapp'.d("$todos=" //   $todos    "" 


Sie wird also nichts blockieren und die gesamte Vorlage wird funktionieren - allerdings vorerst mit einer leeren „To-Do-Liste“. Mit einem langweiligen Einstiegsbildschirm können Sie $todos jetzt sicher Ă€ndern, indem Sie eine To-Do-Liste hochladen. #todoapp Sie dazu diesen Nachkommen zu #todoapp

  ,'loader' .u("$todos=todos:query") //  $todos,       .d("u") //   (u-)    


Dieses Element hat eine U-Regel, die genau so aussieht wie die Blockierungsregel, die wir abgelehnt haben, aber es gibt einen grundlegenden Unterschied.
Lassen Sie mich daran erinnern, dass die D-Regel (von unten ) die Elementgenerierungsregel ist, die ausgefĂŒhrt wird, wenn die Vorlage von oben nach unten erstellt wird , vom ĂŒbergeordneten Element bis zu den untergeordneten Elementen. und U-Regeln (von oben ) sind Reaktionsregeln, die als Reaktion auf ein Ereignis ausgefĂŒhrt werden, das von unten nach oben und von Kind zu Eltern auftaucht.
Wenn also einer Variablen in der D-Regel etwas (einschließlich „nichts“) zugewiesen ist, bedeutet dies, dass sie im GĂŒltigkeitsbereich dieses Elements deklariert und initialisiert wird und ihre Nachkommen (verschachtelte GĂŒltigkeitsbereiche werden wie in JS in DAP implementiert) ) Zuweisung in up-rules bedeutet eine Änderung einer Variablen, die zuvor im GĂŒltigkeitsbereich deklariert wurde. Die Deklaration und Initialisierung von Variablen in der d-Regel ermöglicht es dem ĂŒbergeordneten Element, die fĂŒr die Konstruktion erforderlichen Informationen an die untergeordneten Elemente in der Hierarchie weiterzugeben, und die Änderung ermöglicht es dem ĂŒbergeordneten Element, Aktualisierungen an diese Informationen weiterzugeben und so eine angemessene Umstrukturierung aller davon abhĂ€ngigen Elemente einzuleiten.

Das loader Element, das ein Abkömmling von #todoapp , #todoapp in seiner U-Regel die Variable $todos und lĂ€dt Daten vom Server hinein, wodurch alle Consumer-Elemente dieser Variablen (und nur diese, was wichtig ist!) Automatisch neu #todoapp . Verbraucher einer Variablen sind Elemente, deren D-Regeln diese Variable als einen Wert enthalten, d.h. Diejenigen, die diese Variable (unter BerĂŒcksichtigung des GĂŒltigkeitsbereichs) beim Erstellen lesen .

Wir haben jetzt einen Konsumenten der Variablen $todos - die eigentliche UL#todo-list , die dementsprechend nach dem Laden der Daten neu erstellt wird.

  ,'UL#todo-list'.d("*@ $todos" //  ,   $todos 


Jetzt haben wir also eine #todoapp , die eine Statusvariable in #todoapp , ohne das anfÀngliche Rendern der Vorlage zu blockieren.

10. Löschen und HinzufĂŒgen von Aufgaben


Jetzt können wir $todos jeder Hinsicht Àndern. Beginnen wir mit dem Entfernen der Elemente. Wir haben bereits einen Button-Cross BUTTON.destroy , der die Server-Entfernungsanfragen bisher einfach sendet

  ,'BUTTON.destroy'.ui("(@method`DELETE .url:dehttp):query") 


Es muss sichergestellt werden, dass das entsprechende Objekt auch aus der Variablen $todos gelöscht wird. Da dies eine Änderung ist, wird UL#todo-list als Consumer dieser Variablen automatisch neu erstellt, jedoch ohne das gelöschte Element.

Dap bietet an sich keine speziellen Möglichkeiten zur Manipulation von Daten. Manipulationen können perfekt in Funktionen in JS geschrieben werden, und DAP-Regeln liefern ihnen einfach Daten und nehmen das Ergebnis auf. Wir schreiben eine JS-Funktion, um ein Objekt aus einem Array zu entfernen, ohne seine Nummer zu kennen. Zum Beispiel das:

 const remove = (arr,tgt)=> arr.filter( obj => obj!=tgt ); 


Sie können wahrscheinlich etwas effektiveres schreiben, aber darum geht es jetzt nicht. Es ist unwahrscheinlich, dass unsere Anwendung mit Aufgabenlisten von Millionen von Elementen arbeiten muss. Wichtig ist, dass die Funktion ein neues Array-Objekt zurĂŒckgibt und nicht nur das Element von dem entfernt, was es ist.

Um diese Funktion ĂŒber DAP-Regeln zugĂ€nglich zu machen, mĂŒssen Sie sie dem Abschnitt .FUNC hinzufĂŒgen, aber vorher entscheiden, wie wir sie aufrufen möchten. In diesem Fall besteht die einfachste Möglichkeit darin, sie vom Konverter aufzurufen, der das Objekt { todos, tgt } akzeptiert und ein gefiltertes Array zurĂŒckgibt

 .FUNC({ convert:{ dehttp: url => url.replace(/^https?\:/,''), //        remove: x => remove(x.todos,x.tgt) //     } }) 


Aber nichts .FUNC Sie daran, diese Funktion direkt in .FUNC (Ich habe bereits gesagt, dass .FUNC eine regulÀre JS-Methode ist und das Argument ein regulÀres JS-Objekt.)

 .FUNC({ convert:{ dehttp: url => url.replace(/^https?\:/,''), remove: x => x.todos.filter( todo => todo!=x.tgt ) } }) 


Jetzt können wir ĂŒber die DAP-Regeln auf diesen Konverter zugreifen

  ,'BUTTON.destroy' .ui("$todos=($todos $@tgt):remove (@method`DELETE .url:dehttp):query") 


Hier bilden wir zuerst ein Objekt, das in der JS-Notation mit { todos, tgt:$ } , ĂŒbergeben es an den in .FUNC beschriebenen :remove Konverter und geben das gefilterte Ergebnis an $todos , wodurch es .FUNC wird. Hier ist $ der Datenkontext des Elements, des GeschĂ€ftsobjekts aus dem Array $todos auf dem die Vorlage basiert. Nach dem @ -Symbol wird der Alias ​​des Arguments angezeigt. Wenn @ fehlt, wird der eigene Name des Arguments verwendet. Dies Ă€hnelt der kĂŒrzlichen AbkĂŒrzung von ES6 Innovation - Property .

In Ă€hnlicher Weise fĂŒgen wir der Liste einen neuen Fall hinzu, indem wir das Element INPUT#new-todo und die POST-Anforderung verwenden

  ,'INPUT#new-todo placeholder="What needs to be done?" autofocus' .ui("$=(#.value@title) (@method`POST todos@url headers $):query $todos=($todos $@tgt):insert #.value=") ... .FUNC({ convert:{ dehttp: url => url.replace(/^https?\:/,''), remove: x => x.todos.filter( todo => todo!=x.tgt ), //     insert: x => x.todos.concat( [x.tgt] ) //     } }) 


Die Reaktionsregel des INPUT#new-todo Elements auf ein Standard-UI-Ereignis (fĂŒr die INPUT Elemente wird das change Ereignis als Standard-DAP betrachtet) umfasst Folgendes: Lesen von Benutzereingaben aus der value Eigenschaft dieses Elements, Bilden eines lokalen $ .title mit diesem Wert als .title Feld, Senden von $ .title Ändern Sie mit der POST-Methode das Array $todos indem Sie den Kontext $ als neues Element hinzufĂŒgen und schließlich die value Eigenschaft des INPUT Elements INPUT .

Hier könnte ein junger Leser fragen: Warum sollte concat() wenn ein Element zu einem Array hinzugefĂŒgt wird, wenn dies mit concat() push() ? Ein erfahrener Leser wird sofort verstehen, was los ist, und seine Antwort in die Kommentare schreiben.

Wir schauen uns an, was passiert ist: FĂ€lle werden normal hinzugefĂŒgt und gelöscht, die entsprechenden Anfragen werden korrekt an den Server gesendet (Sie lassen die Registerkarte Netzwerk die ganze Zeit geöffnet, oder?). Aber was ist, wenn wir den Namen oder Status eines neu hinzugefĂŒgten Falls Ă€ndern möchten? Das Problem besteht darin, dass wir zum Benachrichtigen des Servers ĂŒber diese Änderungen .url benötigen, mit der der Server diesem Unternehmen .url wird. Als wir das GeschĂ€ft erstellt haben, kannten .url die .url bzw. wir können nicht die richtige PATCH-Änderungsanforderung erstellen.

TatsĂ€chlich sind alle erforderlichen Informationen zu dem Fall in der Antwort des Servers auf die POST-Anforderung enthalten. Es wĂ€re richtiger, ein neues GeschĂ€ftsobjekt nicht nur aus Benutzereingaben, sondern auch aus der Antwort des Servers zu erstellen und dieses Objekt mit allen bereitgestellten Informationen zu $todos hinzuzufĂŒgen Serverinformationen, einschließlich des .url

  ,'INPUT#new-todo placeholder="What needs to be done?" autofocus' .ui("$todos=($todos (@method`POST todos@url headers (#.value@title)):query@tgt ):insert #.value=") 


Wir sehen aus - okay, jetzt wird alles richtig ausgearbeitet. Benachrichtigungen an den Server ĂŒber die Bearbeitung von neu erstellten FĂ€llen sind in Ordnung.

Man könnte damit aufhören, aber ... Wenn Sie genau hinschauen, können Sie immer noch eine leichte Verzögerung zwischen der Eingabe des Namens des neuen Falls und dem Moment feststellen, in dem er in der Liste erscheint. Diese Verzögerung ist deutlich sichtbar, wenn Sie ein simuliertes langsames Netzwerk einschalten. Wie Sie vielleicht erraten haben, handelt es sich um eine Anfrage an den Server: Zuerst fordern wir Daten fĂŒr einen neuen Fall vom Server an, und erst nach Erhalt Ă€ndern wir $todos . Im nĂ€chsten Schritt werden wir versuchen, diese Situation zu korrigieren, aber zuerst werde ich Ihre Aufmerksamkeit auf einen anderen interessanten Punkt lenken. Wenn wir ein wenig zur vorherigen Version zurĂŒckkehren , stellen wir fest: Obwohl die Anforderung auch vorhanden ist, wird die neue Groß- / Kleinschreibung sofort zur Liste hinzugefĂŒgt, ohne auf das Ende der Anforderung zu warten

  //    , :query   .ui("$=(#.value@title) (@method`POST todos@url headers $):query $todos=($todos $@tgt):insert #.value=") 


Dies ist ein weiteres Merkmal beim Ausarbeiten von Asynchronkonvertern in dap: Wenn das Ergebnis des Asynchronkonverters nicht verwendet wird (d. H., Es ist keiner Zuordnung zugeordnet), können Sie nicht auf seinen Abschluss warten - und die AusfĂŒhrung der Regel wird nicht blockiert. Dies ist hĂ€ufig hilfreich: Sie haben möglicherweise bemerkt, dass beim Löschen von FĂ€llen aus der Liste diese sofort vom Bildschirm verschwinden, ohne auf das Ergebnis der DELETE-Anforderung zu warten. Dies macht sich insbesondere dann bemerkbar, wenn Sie schnell mehrere FĂ€lle hintereinander löschen und Anforderungen im Netzwerkfenster nachverfolgen.

Da wir jedoch das Ergebnis der POST-Anforderung verwenden - es dem $ -Kontext zuweisen - mĂŒssen wir warten, bis der Vorgang abgeschlossen ist. Daher mĂŒssen Sie einen anderen Weg finden, um $todos zu Ă€ndern, bevor Sie die POST-Anforderung ausfĂŒhren. Lösung: Erstellen Sie dennoch zuerst ein neues GeschĂ€ftsobjekt und fĂŒgen Sie es sofort zu $todos , lassen Sie die Liste zeichnen, und fĂŒhren Sie erst dann nach dem Rendern eine POST-Anforderung aus, wenn das GeschĂ€ft .url ( .url das GeschĂ€ft wurde gerade erstellt) Das Ergebnis wird dem Datenkontext dieses Falls auferlegt.

Als erstes fĂŒgen wir der .title ein Leerzeichen hinzu, das nur .title enthĂ€lt

  ,'INPUT#new-todo placeholder="What needs to be done?" autofocus' .ui("$todos=($todos (#.value@title)@tgt):insert #.value=") 


UL#todo-list > LI Regel zur Erzeugung von UL#todo-list > LI Elementen enthĂ€lt bereits a! Starten einer A-Regel nach dem erstmaligen Zeichnen des Elements. Dort können wir den Start einer POST-Anfrage in Abwesenheit von .url . Um zusĂ€tzliche Felder in den Kontext einzufĂŒgen, hat dap den Operator &

  .a("!? $completed $editing; ? .url:!; & (@method`POST todos@url headers $):query") 


Wir schauen Eine andere Sache! Selbst in einem langsamen Netzwerk wird die Aufgabenliste sofort aktualisiert und die Serverbenachrichtigung und das Laden fehlender Daten erfolgt nach dem Zeichnen der aktualisierten Liste im Hintergrund.

11. Daw jeder!


Im Element #header befindet sich fĂŒr alle FĂ€lle in der Liste eine SchaltflĂ€che zum Masseninstallieren / ZurĂŒcksetzen des Abschlusszeichens. FĂŒr die Massenzuweisung von Werten zu den Feldern der Elemente des Arrays schreiben wir einfach einen anderen Konverter :assign und auf $todos anwenden, indem Sie auf INPUT#toggle-all klicken

  ,'INPUT#toggle-all type=checkbox' .ui("$todos=($todos (#.checked@completed)@src):assign") ... assign: x => x.todos && x.todos.map(todo => Object.assign(todo,x.src)) 


In diesem Fall interessiert uns nur das Feld .completed , aber es ist leicht zu erkennen, dass Sie mit einem solchen Konverter die Werte aller Felder der Array-Elemente massiv Àndern können.
Ok, im $todos Array sind die $todos umgeschaltet, jetzt mĂŒssen wir den Server ĂŒber die vorgenommenen Änderungen informieren. Im ursprĂŒnglichen Beispiel erfolgt dies durch Senden von PATCH-Anfragen fĂŒr jeden Fall - keine sehr effektive Strategie, aber es hĂ€ngt nicht mehr von uns ab. Ok, fĂŒr jeden Fall senden wir eine PATCH-Anfrage

  .ui("*@ $todos=($todos (#.checked@completed)@src):assign; (@method`PATCH .url:dehttp headers (.completed)):query") 


Wir schauen : Ein Klick auf eine gemeinsame Daw richtet alle einzelnen Daws aus und der Server wird durch entsprechende PATCH-Anfragen benachrichtigt. Norm

12. FĂ€lle nach Fertigstellung filtern


Neben der eigentlichen Aufgabenliste sollte die Anwendung auch in der Lage sein, FĂ€lle nach dem Abschlusszeichen zu filtern und ZĂ€hler fĂŒr abgeschlossene und nicht abgeschlossene Aufgaben anzuzeigen. NatĂŒrlich ist es fĂŒr das Filtern trivial, dieselbe filter() -Methode zu verwenden, die von JS selbst bereitgestellt wird.

Aber zuerst mĂŒssen Sie sicherstellen, dass das Feld " .completed fĂŒr jeden Fall immer "true" ist, und wenn Sie auf die einzelne Tagesordnung des Falls klicken, wird diese zusammen mit der Variablen " $completed aktualisiert. Bisher war uns das nicht wichtig, aber jetzt wird es so sein.

  ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=(.completed=$completed=#.checked) $recount=()") //  .completed        


Der wichtige Punkt hierbei ist, dass der Datenkontext jedes Falls das Case-Objekt selbst ist, das im Array $todos . Keine einzige Kopie oder verwandte Konstruktion, sondern das Objekt selbst. Und alle Aufrufe der Felder .title , .completed url - sowohl Lese- als auch Schreibzugriff - gilt direkt fĂŒr dieses Objekt. Damit die Filterung des $todos Arrays ordnungsgemĂ€ĂŸ funktioniert, muss die VervollstĂ€ndigung des Falls nicht nur in der MorgendĂ€mmerung auf dem Bildschirm, sondern auch im .completed Feld des .completed Objekts .completed werden.

Um nur FĂ€lle mit dem erforderlichen VollstĂ€ndigkeitszeichen in der Liste .completed , filtern wir $todos einfach nach dem ausgewĂ€hlten Filter. Der ausgewĂ€hlte Filter ist, Sie haben es erraten, eine weitere Zustandsvariable unserer Anwendung, und wir werden ihn $filter . Um $todos nach dem ausgewĂ€hlten $filter gehen wir die Miniaturansicht entlang und fĂŒgen einfach einen weiteren Konverter der Form {list, filter} => der gefilterten Liste hinzu , und wir werden die Namen und Filterfunktionen aus dem "assoziativen Array" (d. H. Gewöhnlichen JS) ĂŒbernehmen object) todoFilters

 const todoFilters={ "All": null, "Active": todo => !todo.completed, "Completed": todo => !!todo.completed }; '#todoapp'.d("$todos= $filter=" //   $filter ... ,'UL#todo-list'.d("* ($todos $filter):filter" ... ,'UL#filters'.d("* filter" //  filter      .DICT ,'LI' .d("! .filter") .ui("$filter=.") //    "$filter=.filter" ) ... .DICT({ ... filter: Object.keys(todoFilters) //["All","Active","Completed"] }) .FUNC({ convert:{ ... filter: x =>{ const a = x.todos, f = x.filter && todoFilters[x.filter]; return a&&f ? a.filter(f) : a; } } }) 


Wir prĂŒfen . Filter funktionieren einwandfrei. Eine Nuance besteht darin, dass die Namen der Filter zusammen angezeigt werden, weil Hier haben wir uns ein wenig von der DOM-Struktur des Originals entfernt und sind aus CSS herausgekommen. Aber wir werden spĂ€ter darauf zurĂŒckkommen.

13. ZĂ€hler abgeschlossener und aktiver FĂ€lle.


Um die ZĂ€hler abgeschlossener und aktiver FĂ€lle anzuzeigen, filtern Sie einfach $todos mit den entsprechenden Filtern und zeigen die LĂ€ngen der resultierenden Arrays an

  ,'#footer'.d("$active=($todos @filter`Active):filter $completed=($todos @filter`Completed):filter" ,'#todo-count'.d("! (active $active.length)format") //  length    active ... ,'#clear-completed'.d("! (completed $completed.length)format") ) ... .DICT({ ... active: "{length} items left", completed: "Clear completed items ({length})" }) 


In diesem Formular zeigen die Leistungsindikatoren beim Booten die korrekten Werte an, reagieren jedoch nicht auf spĂ€tere Änderungen beim Abschluss von VorgĂ€ngen (wenn Sie auf die Tagesordnung klicken). Tatsache ist, dass Klicks auf Dohlen, die den Status jedes einzelnen Falls Ă€ndern, den Status von $todos nicht Ă€ndern - eine Änderung eines Array-Elements ist keine Änderung des Arrays. Daher benötigen wir ein zusĂ€tzliches Signal fĂŒr die Notwendigkeit, FĂ€lle neu zu registrieren. Ein solches Signal kann eine zusĂ€tzliche Zustandsvariable sein, die jedes Mal geĂ€ndert wird, wenn ein erneutes ZĂ€hlen erforderlich ist. Nennen wir es $recount . d- , , #footer — d-

 '#todoapp'.d("$todos= $filter= $recount=" //  $recount     ... ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=(.completed=$completed=#.checked) $recount=()") //  $recount    ... ,'#footer'.d("$active=($todos @filter`Active):filter $completed=($todos @filter`Completed):filter $recount" //  $recount 


, .

14. .


TodoMVC , — . , , , DELETE- — $completed . , $todos , $active

  ,'#clear-completed' .d("! (completed $completed.length)format") .ui("$todos=$active; *@ $completed; (@method`DELETE .url:dehttp):query") 


: , . Network .

15.


. #. #- — . URL .

location.hash urlhash , , a- #todoapp ( ), $filter

 .a("urlhash $filter") 


$filter hashchange - :urlhash , location.hash ( #)

 .d("$todos= $filter=:urlhash $recount=" .e("hashchange","$filter=:urlhash") 


hashchange #- . , - window document.body . #todoapp , d- listen , window

 '#todoapp' .a("urlhash $filter") .e("hashchange","$filter=:urlhash") .d("$todos= $filter=:urlhash $recount=; listen @hashchange" 


: , , #Active , #All , #Completed . Alles arbeitet. . , , — . , —

  ,'UL#filters'.d("* filter" ,'LI'.d("" ,'A'.d("!! (`# .filter)concat@href .filter@") ) ) 


, !? , CSS- selected , .filter $filter

  ,'A'.d("!! (`# .filter)concat@href .filter@; !? (.filter $filter)eq@selected") 


dap- ( ) , .

16.


, , head HTML-

  [ui=click]{cursor:pointer} 


, .

, ! «todos». , , - , «todos» «dap todos»

  ,'H1'.d("","dap todos") 


. , ( ).

Abschließend


, , dap- — «, », « , » .. . . , , , .

, , .

Source: https://habr.com/ru/post/de481680/


All Articles