Dap in Aktion. TodoMVC schreiben. Teil 1

Der erste Artikel über DAP wurde offensichtlich nicht zu meinem Erfolg beim Schreiben: Die überwiegende Mehrheit der Kommentare dazu lautete "Niasilil" und "Niasilil, aber ich verurteile". Und der Preis für den einzigen konstruktiven Top-Level-Kommentar geht an OldVitus , um Ratschläge zu erhalten, wie die Dap am Beispiel von TodoMVC demonstriert werden kann, damit es etwas zu vergleichen gibt. Was mache ich in diesem Artikel?

TodoMVC , falls jemand es nicht weiß, ist dies eine solche Standard-UI- Aufrufwelt , mit der Sie Lösungen für dasselbe Problem - die bedingte "To-Do-Liste" - unter Verwendung verschiedener Frameworks vergleichen können. Die Aufgabe ist bei all ihrer Einfachheit (ihre Lösung für Dap Breaks "in einem Bildschirm") sehr anschaulich. Ich werde daher anhand ihres Beispiels versuchen zu zeigen, wie die für ein Web-Frontend typischen Aufgaben mit dap umgesetzt werden.

Ich habe die formale Beschreibung des Problems nicht gesucht und studiert, sondern mich dazu entschlossen, eines der Beispiele einfach umzukehren. Das Backend für diesen Artikel ist für uns nicht interessant, daher schreiben wir es nicht selbst, sondern verwenden eines der fertigen von der Website www.todobackend.com . Von dort nehmen wir einen Beispielclient und eine Standard- CSS-Datei .

Um dap zu verwenden, müssen Sie nichts herunterladen und installieren. Keine npm install und das ist alles. Es ist nicht erforderlich, Projekte mit einer bestimmten Verzeichnisstruktur, Manifesten und anderen Attributen für den IT-Erfolg zu erstellen. Genug Texteditor und Browser. Zum Debuggen von XHR-Anforderungen benötigen Sie möglicherweise auch einen ausreichend einfachen Webserver, z. B. diese Erweiterung für Chrome. Unser gesamtes Frontend besteht aus einer einzigen .html-Datei (natürlich unter Bezugnahme auf das DAP-Engine-Skript und die Standard-TodoMVC-CSS-Datei).

Also von Grund auf neu.

1. Erstellen Sie eine HTML-Datei


 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Todo -- dap sample</title> <link rel="stylesheet" href="https://www.todobackend.com/client/css/vendor/todomvc-common.css"/> <script src="https://dap.js.org/0.4.js"></script> </head> <body> <script> //   dap </script> </body> </html> 

Die übliche HTML-Vorbereitung, in der wir die CSS-Datei, die freundlicherweise von der Site www.todobackend.com bereitgestellt wird, und die DAP-Engine, die ebenfalls freundlicherweise von der Site dap.js.org bereitgestellt wird, enthalten

2. Kopieren Sie die DOM-Struktur des ursprünglichen Beispiels


Um die Standard-CSS-Datei unverändert zu verwenden, wird dieselbe DOM-Struktur wie im ursprünglichen Beispiel verwendet . Öffnen Sie es im Chrome-Browser, drücken Sie Strg + Umschalt + I, wählen Sie die Registerkarte "Elemente" und stellen Sie sicher, dass sich die Anwendung selbst im Elementabschnitt section id="todo-app">



Indem wir diesen Teilbaum einzeln öffnen, schreiben wir seine Struktur in unsere .html-Datei. Jetzt skizzieren wir einfach auf schnelle Weise und schreiben keinen Code. Deshalb schreiben wir die Signaturen der Elemente einfach in einfache Anführungszeichen und in Klammern ihrer Kinder. Wenn es keine Kinder gibt, zeichnen wir leere Klammern. Wir überwachen die Indizes und das Gleichgewicht der Klammern.

 //   dap '#todoapp'( '#header'( 'H1'() 'INPUT#new-todo placeholder="What needs to be done?" autofocus'() ) '#main'( '#toggle-all type=checkbox'() 'UL#todo-list'( 'LI'( 'INPUT.toggle type=checkbox'() 'LABEL'() 'BUTTON.destroy'() ) ) ) '#footer'( '#todo-count'() 'UL#filters'( 'LI'() ) '#clear-completed'() ) ) 

Hinweis: Wenn Sie Elemente wiederholen (hier sind es beispielsweise LI Elemente), schreiben Sie einmal in die Struktur, auch wenn das Original mehrere Elemente enthält. Offensichtlich sind dies Arrays aus dem gleichen Muster.

Ich denke, das Signaturformat ist für jeden verständlich, der HTML und CSS mit den Händen geschrieben hat, daher werde ich mich im Moment nicht näher damit befassen. Ich kann nur sagen, dass Tags in GROSSBUCHSTABEN geschrieben sind und das Fehlen eines Tags dem Vorhandensein eines DIV-Tags entspricht. Die Fülle von # -Elementen (mit id) ist hier auf die Besonderheiten der enthaltenen CSS-Datei zurückzuführen, die hauptsächlich id-Selektoren verwendet.

3. Denken Sie daran, dass das DAP-Programm Javascript ist


Um uns unnötige Klammern im Code zu ersparen, String.prototype Engine mehrere Methoden direkt in String.prototype (mir ist bewusst, dass die Implementierung Ihrer Methoden in Standardobjekten ein Problem ist, aber ... kurz gesagt, wir haben es bestanden), das die Signaturzeichenfolge in DAP konvertiert vorlage. Eine solche Methode ist .d(rule, ...children) . Das erste Argument, das es braucht, ist eine Generierungsregel ( d-Regel ), und der Rest der Argumente ist eine willkürliche Anzahl von untergeordneten Elementen.

Basierend auf diesem neuen Wissen fügen wir unseren Code so hinzu, dass wir anstelle jeder öffnenden Klammer die Sequenz .d("" haben und vor jedem öffnenden einfachen Anführungszeichen, mit Ausnahme des allerersten, ein Komma steht. Life Hack: Sie können die automatische Ersetzung verwenden.

 '#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("" ,'LI'.d("" ,'INPUT.toggle type=checkbox'.d("") ,'LABEL'.d("") ,'BUTTON.destroy'.d("") ) ) ) ,'#footer'.d("" ,'#todo-count'.d("") ,'UL#filters'.d("" ,'LI'.d("") ) ,'#clear-completed'.d("") ) ) 

Voila! Wir haben eine .d von Aufrufen für die .d Methode erhalten, die in eine DAP-Vorlage umgewandelt werden kann. Die leeren Zeichenfolgen "" sind die Keime zukünftiger D-Regeln, und die untergeordneten Zeichen sind durch Kommas getrennte Argumente. Formal ist dies ein gültiges DAP-Programm, wenn auch noch nicht vollständig mit dem Auspuff, den wir brauchen. Aber es kann schon gestartet werden! .RENDER() Sie dazu nach der schließenden .RENDER() die .RENDER() Methode hinzu. Diese Methode rendert, wie der Name schon sagt, die resultierende Vorlage.

Zu diesem Zeitpunkt liegt also eine HTML-Datei mit folgendem Inhalt vor:

 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Todo -- dap sample</title> <link rel="stylesheet" href="https://www.todobackend.com/client/css/vendor/todomvc-common.css"/> <script src="https://dap.js.org/0.4.js"></script> </head> <body> <script> '#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("" ,'LI'.d("" ,'INPUT.toggle type=checkbox'.d("") ,'LABEL'.d("") ,'BUTTON.destroy'.d("") ) ) ) ,'#footer'.d("" ,'#todo-count'.d("") ,'UL#filters'.d("" ,'LI'.d("") ) ,'#clear-completed'.d("") ) ) .RENDER() //   dap   </script> </body> </html> 

Sie können es in einem Browser öffnen , um sicherzustellen, dass DOM-Elemente generiert und CSS-Stile angewendet werden. Sie müssen diese Vorlage nur noch mit Daten füllen.

4. Holen Sie sich die Daten


Wir gehen zur Originalseite , öffnen die Registerkarte Netzwerk in den Tools, aktivieren den XHR-Filter und sehen, woher die Daten kommen und in welcher Form.





Okay, okay. Die Aufgabenliste wird direkt von todo-backend-express.herokuapp.com als JSON- Array von Objekten übernommen. Großartig

Um Daten zu empfangen, verfügt dap über einen eingebauten Konverter :query der die URL asynchron in die von ihm empfangenen Daten "konvertiert". Wir werden die URL selbst nicht direkt in die Regel schreiben, sondern sie mit den konstanten todos . Dann sieht das gesamte Data Mining-Design folgendermaßen aus:

 todos:query 

und schreiben Sie die todos Konstante selbst in das Wörterbuch - im Abschnitt .DICT direkt vor .RENDER() :

 '#todoapp'.d("" ... ) .DICT({ todos : "https://todo-backend-express.herokuapp.com/" }) .RENDER() 

Nachdem wir das Array todos , erstellen wir daraus eine .title -Do-Liste: Für jeden Fall nehmen wir den Namen aus dem Feld .title und schreiben ihn in das Element LABEL , und aus dem Feld .completed nehmen .completed das Vorzeichen für „Vollständigkeit“ und schreiben in die Eigenschaft checked des checked INPUT.toggle . Es ist so gemacht:

  ,'UL#todo-list'.d("*@ todos:query" //  *       ,'LI'.d("" ,'INPUT.toggle type=checkbox'.d("#.checked=.completed") // #  " " ,'LABEL'.d("! .title") //  !      ,'BUTTON.destroy'.d("") ) ) 

Wir aktualisieren unsere Seite im Browser und ... wenn Sie sie vom Dateisystem aus starten, passiert nichts. Das Problem ist, dass moderne Browser keine domänenübergreifenden XHR-Anforderungen aus lokalen Dokumenten zulassen.



Es ist Zeit, unsere Seite über http anzuschauen - mit einem beliebigen lokalen Webserver. Nun, oder wenn Sie nicht bereit sind, DAP mit Ihren eigenen Händen zu schreiben, sehen Sie sich die aufeinander folgenden Versionen der Seite unter Verwendung meiner Links an (vergessen Sie nicht, den Quellcode zu betrachten - in Chrome geschieht dies mit Strg + U).

Also gehen wir zu unserer Seite unter http: // und sehen, dass die Daten kommen und die Liste erstellt wird. Großartig! Sie haben die Operatoren * und bereits gemeistert ! , converter :query , Konstanten und Zugriff auf die Felder des aktuellen Array-Elements. Schauen Sie sich den resultierenden Code noch einmal an. Scheint es Ihnen immer noch unlesbar?

5. Zustand hinzufügen


Möglicherweise haben Sie bereits versucht, auf die Häkchen in der Aufgabenliste zu klicken. Die Kontrollkästchen selbst ändern die Farbe, aber im Gegensatz zum Original ändert das übergeordnete LI Element seinen Stil nicht (der „erledigte Auftrag“ sollte grau und durchgestrichen sein, dies geschieht jedoch nicht) - die Dinge ändern ihren Status nicht . Diese Elemente haben jedoch noch keinen Status und können ihn dementsprechend nicht ändern. Jetzt werden wir es reparieren.

Fügen Sie dem LI Element den Status "Abgeschlossen" hinzu. Definieren Sie dazu in seiner D-Regel die Variable $completed state . Dem INPUT.toggle , das diesen Status ändern kann, wird eine entsprechende Reaktionsregel ( ui-rule ) zugewiesen, mit der die Variable $completed entsprechend ihrem eigenen aktivierten Flag gesetzt wird ("daw ist eingeschaltet"). Abhängig vom Status von $completed aktiviert oder deaktiviert das LI Element die CSS-Klasse "completed".

  ,'UL#todo-list'.d("*@ todos:query" ,'LI'.d("$completed=.completed"//  ,    .completed ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") //       .ui("$completed=#.checked") //    $completed ,'LABEL'.d("! .title") ,'BUTTON.destroy'.d("") ) .a("!? $completed") //     $completed,    css- completed ) 

Solche Manipulationen mit CSS-Klassen sind ziemlich verbreitet, es gibt also einen speziellen Operator in dap für sie !?
Bitte beachten Sie, dass wir dies in der Regel tun (ab dem Wort akkumulieren). Warum nicht in der D-Regel? Der Unterschied zwischen diesen beiden Regeltypen besteht darin, dass die d-Regel beim Aktualisieren den Inhalt des Elements vollständig neu erstellt und dabei den alten und den neuen Inhalt wieder löscht, während die a-Regel den vorhandenen Inhalt des Elements nicht berührt, sondern das Ergebnis an das anfügt, was bereits vorhanden ist. Das Ändern eines einzelnen Attributs eines LI Elements erfordert keine Umstrukturierung des restlichen Inhalts. Daher ist es rationaler, dies in der Regel zu tun.

Wir schauen uns das Ergebnis an . Es ist schon besser: Wenn Sie auf die Kontrollkästchen klicken, ändert sich der Status des entsprechenden Aufgabenelements, und entsprechend ändert sich auch der visuelle Stil des Elements. Es besteht jedoch weiterhin ein Problem: Wenn die Liste der abgeschlossenen Aufgaben ursprünglich vorhanden war, werden sie nicht grau angezeigt, da die a-Regel standardmäßig nicht ausgeführt wird, wenn das Element generiert wird. Um es auch während der Generierung auszuführen, fügen wir den Operator a! Zur d-Regel des Elements LI

  ,'LI'.d("$completed=.completed; a!" //      $completed    a- 

Wir schauen Ok Mit dem Status von $completed herausgefunden. Abgeschlossene Fälle werden sowohl beim ersten Systemstart als auch beim anschließenden manuellen Umschalten korrekt stilisiert.

6. Bearbeiten von Fallnamen


Zurück zum Original . Durch Doppelklick auf den Namen des Falls wird der Bearbeitungsmodus aktiviert, in dem dieser Name geändert werden kann. Dort wird es so implementiert, dass die Ansichtsmodusvorlage „Ansicht“ (mit einer Morgendämmerung, einem Titel und einer Löschschaltfläche) vollständig ausgeblendet wird und das Element INPUT class="edit" angezeigt wird. Wir werden es etwas anders machen - wir werden nur das LABEL Element verbergen, da die anderen beiden Elemente unsere Bearbeitung nicht stören. LABEL einfach die view zum LABEL Element hinzu.

Definieren Sie für den Status "Bearbeitung" die Variable " $editing im LI Element. Anfangs wird es (Zustand) zurückgesetzt, durch dblclick auf das LABEL Element LABEL und LABEL , wenn das INPUT.edit Element nicht mehr im Fokus steht. Also schreiben wir:

  ,'LI'.d("$completed=.completed $editing=; a!" //       ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$completed=#.checked") ,'LABEL.view' .d("? $editing:!; ! .title") //  $editing ,     .e("dblclick","$editing=`yes") //  dblclick  $editing ,'INPUT.edit' .d("? $editing; !! .title@value") //  $editing  .ui(".title=#.value") //  .title   change (ui     INPUT) .e("blur","$editing=") //  $editing   blur ,'BUTTON.destroy'.d("") ).a("!? $completed $editing") //   $completed  $editing  css-  'LI' 

Jetzt können wir die Namen der Fälle bearbeiten.

7. Senden von Daten an den Server


Ok, wir können bereits Dinge im Browser bearbeiten, aber diese Änderungen müssen auch auf den Server übertragen werden. Wir schauen, wie das Original es macht:



Die Änderungen werden mithilfe der PATCH-Methode mit einer bestimmten URL in der Form http://todo-backend-express.herokuapp.com/28185 an den Server gesendet, die natürlich für jeden Fall eindeutig ist. Diese URL wird vom Server im Feld .url für jeden Fall in der Liste angegeben. Das heißt, wir müssen lediglich eine PATCH-Anforderung an die im Feld .url angegebene Adresse .url , und zwar mit den geänderten Daten im JSON-Format:

  ,'INPUT.edit' .d("? $editing; !! .title@value") .ui(".title=#.value; (@method`PATCH .url (@Content-type`application/json)@headers (.title):json.encode@body):query") .e("blur","$editing=") 

Hier verwenden wir den gleichen Konverter :query , jedoch in einer detaillierteren Version. Wenn :query auf eine einfache Zeichenfolge angewendet wird, wird diese Zeichenfolge als URL behandelt und eine GET-Anforderung ausgeführt. Wenn :query ein komplexes Objekt empfängt, wie in diesem Fall, wird es als detaillierte Beschreibung der Anfrage mit den Feldern .method , .url , .headers und .body und die Anfrage entsprechend ausgeführt. Hier senden .title unmittelbar nach der Aktualisierung von .title eine PATCH-Anfrage mit diesem aktualisierten .title den Server

Aber es gibt eine Nuance. Wir bekommen das .url Feld vom Server, es sieht .url aus: http://todo-backend-express.herokuapp.com/28185 , .url das http: // -Protokoll ist darin fest codiert, wenn unser Client auch über http: // geöffnet ist dann ist alles in ordnung. Wenn der Client jedoch über https: // geöffnet ist, tritt ein Problem auf: Aus Sicherheitsgründen blockiert der Browser den http-Verkehr von der https-Quelle.

Es ist einfach gelöst: Wenn Sie das Protokoll aus .url entfernen, .url die Anforderung das .url . Also lasst es uns tun: Schreiben Sie den entsprechenden Konverter - dehttp , und wir werden .url durchlaufen. Benutzerdefinierte Konverter (und andere Funktionen) werden im Abschnitt .FUNC :

  .ui(".title=#.value; (@method`PATCH .url:dehttp (@Content-type`application/json)@headers (.title):json.encode@body):query") ... .FUNC({ convert:{ //  -         dehhtp: url=>url.replace(/^https?\:/,'')//    URL } }) 

Es ist auch sinnvoll, das Header-Objekt in das Wörterbuch einzufügen, damit es in anderen Abfragen verwendet werden kann:

  .ui(".title=#.value; (@method`PATCH .url:dehttp headers (.title):json.encode@body):query") ... .DICT({ todos : "//todo-backend-express.herokuapp.com/", headers: {"Content-type":"application/json"} }) 

Nun, für Full Feng Shui werden wir eine andere nützliche Eigenschaft des Konverters verwenden :query - automatische Codierung des Anforderungskörpers in json gemäß dem Content-type:application/json Header. Infolgedessen sieht die Regel folgendermaßen aus:

  .ui(".title=#.value; (@method`PATCH .url:dehttp headers (.title)):query") 

Also schau . Okay, Fallnamen ändern sich jetzt nicht nur im Browser, sondern auch auf dem Server. Aber! Es kann sich nicht nur der Name des Falls ändern, sondern auch sein Fertigstellungsgrad - completed . Es muss also auch an den Server gesendet werden.
Sie können dieselbe PATCH-Anfrage zum INPUT.toggle Element hinzufügen. Senden INPUT.toggle einfach (.completed) anstelle von (.completed) :

  ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$completed=#.checked; (@method`PATCH .url:dehttp headers (.completed:?)):query") 

Und Sie können diese PATCH-Anfrage "aus Klammern" setzen:

  ,'LI'.d("$completed=.completed $editing= $patch=; a!" // $patch - ""   ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=($completed=#.checked)") //   $patch  completed ,'LABEL.view' .d("? $editing:!; ! .title") .e("dblclick","$editing=`yes") ,'INPUT.edit' .d("? $editing; !! .title@value") .ui("$patch=(.title=#.value)") //   $patch  title .e("blur","$editing=") ,'BUTTON.destroy'.d("") ) .a("!? $completed $editing") //  $patch  ,   ,   .u("? $patch; (@method`PATCH .url:dehttp headers $patch@):query $patch=") 

Hier ist das Ding. Reaktionsregeln gehören zur Gruppe der „Aufwärtsregeln“, die „von unten nach oben“ ausgeführt werden - vom Nachkommen zum übergeordneten Element bis zur Wurzel selbst (diese Sequenz kann bei Bedarf unterbrochen werden). Dies ähnelt Popup-Ereignissen im DOM. Daher können einige Fragmente der Reaktion, die mehreren Nachkommen gemeinsam sind, einem gemeinsamen Vorfahren zugeordnet werden.

Insbesondere ist in unserem Fall der Vorteil einer solchen Delegierung nicht besonders bemerkbar. Wenn jedoch mehr bearbeitbare Felder vorhanden wären, würde es sehr hilfreich sein, diese umfangreiche Anforderung (natürlich nach DAP-Standards) in eine allgemeine Regel zu packen, um den Code einfach und lesbar zu halten. Also ich empfehle es.

Wir schauen : Nun werden sowohl Namensänderungen als auch Statusänderungen an den Server gesendet.

Wenn Sie im nächsten Artikel interessiert sind, sollten Sie Fälle hinzufügen, entfernen und filtern. In der Zwischenzeit können Sie das Endergebnis und andere Beispiele für DAP- Code unter dap.js.org/docs sehen

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


All Articles