Wenn du Pionierarbeit leistest, wirst du wahrscheinlich an Grenzen stoßen. Wenn du neue Wege gehst, wird deine Lieblingssuchmaschine deine Fragen nicht beantworten können. Hier ist eine kleine Geschichte über meine Versuche, neue Wege bei der Entwicklung von Webanwendungen zu gehen.
Bei der modernen Frontend-Entwicklung dreht sich alles um reaktive JavaScript-Frameworks wie React oder Vue und HTTP-APIs, die Ihnen JSON senden, oder?
Versuchen wir mal etwas anderes!
HTML-Fragmente über die Leitung.
Anstatt JSON an eine im Browser laufende JavaScript-Anwendung zu senden, sendet diese neue Methode HTML an den Client.
Der einfachste Anwendungsfall für htmx ist eine Seite, die drei Formulare enthält. Wenn Sie ein Formular abschicken, sollte nur dieses eine Formular aktualisiert werden. Die beiden anderen Formulare sollen nicht verändert werden.
Dies könnte mit dem htmx-Attribut hx-post
umgesetzt werden:
<form hx-post="/update/foo">…</form>
Der http-Endpunkt /update/foo wird die Formulardaten verarbeiten und dann ein HTML-Fragment zurückgeben.
Das HTML-Fragment würde das <form>-Tag oder ein anderes Tag ersetzen, wenn der Server eine "out of band"-Aktualisierung verwendet.
Wenn Sie mehr darüber erfahren wollen, dann finden Sie alles, was Sie brauchen, unter: htmx.org.
Aber in diesem Artikel geht es nicht um htmx, sondern um Pionierarbeit. Ich werde nun in sieben Schritten aufzeigen, wie ich beim Verlassen des Autopiloten und beim Ausprobieren von etwas Neuem an Grenzen gestoßen bin.
Im Oktober 2020 hörte ich das erste Mal von dieser neuen Art der Entwicklung von Webanwendungen. Nach einigem Googeln und Fragen in Entwicklerkreisen litt ich unter einer „Analyse-Paralyse“, weil es zu viele Optionen gab.
Wenn man Pionier ist, gibt es keine Best Practices. Tools wie Google-Trends oder Stackoverflow-Tagtrend helfen einem nicht weiter, da sie nur die Vergangenheit und nicht die Zukunft zeigen.
Es gab keine einfache Antwort darauf, wie man auf dieser neuen Welle reiten kann. Also habe ich eine einfache Liste von Tools erstellt, die verwendet werden können. Du findest diese Liste in dieser README auf Github. Ich habe mit einigen dieser Tools herumgespielt und über soziale Netzwerke und Foren Feedback eingeholt. Ich entschied mich für htmx, weil es einfach und leicht zu verstehen war.
Nach einigen Wochen des Hin- und Herwanderns habe ich eine Entscheidung getroffen, die ich nicht bereue.
Erkenntnisse:
Ich habe htmx für ein Hobbyprojekt gewählt, das ich für einen Freund entwickelt habe. Eine Django-basierte Anwendung mit PostgreSQL als Datenbank, die auf einem günstigen Virtual Private Server läuft.
Ich könnte stundenlang über die Freude sprechen, die ich bei der Entwicklung eines kleinen Projekts mit diesem neuen Paradigma empfinde. Es ist so leicht und einfach.
Seit 20 Jahren entwickle ich Python-basierte Webanwendungen. Trotzdem benutze ich mehrmals am Tag meine Lieblingssuchmaschine, weil ich schnell eine Antwort finden will (ohne die ganze Dokumentation zu lesen).
Haha. Und jetzt kommt's. Das funktioniert prima, wenn jemand schon einmal über ein ähnliches Problem gestolpert ist, aber das ist nicht der Fall, wenn man Pionierarbeit leistet oder hoch spezialisierte Lösungen entwickelt.
In den ersten Tagen hat alles gut funktioniert. Die Dokumentationen waren gut, und die Beispiele funktionierten gut.
Dann bekam ich diesen Fehler:
Es war zu spät am Abend, und mein Bauchgefühl sagte mir, dass ich heute keine Lösung mehr finden würde. Ich googelte und suchte im Issue Tracker des Projekts: Diese htmx-Fehlermeldung scheint wie ein Geist zu sein. Niemand hat sie bisher gesehen.
In meinem Fall sah das HTML-Fragment, das vom Server erstellt und an den Client gesendet wurde, ungefähr so aus:
<tr>
<td>2</td><td>two</td>
</tr>
<div id="sum" hx-swap-oob="true">MAGIC</div>
Ich habe die htmx-Funktion hx-swap-oob einige Tage lang verwendet. Ich hatte keine Ahnung, warum es hier versagte. Eine kleine Einführung, was dieses HTML-Fragment macht: Es soll einen Teil der aktuellen Seite aktualisieren. Das <tr>
sollte an der Stelle ausgetauscht werden, auf die der Benutzer gerade geklickt hat. Das <div>
aus dem HTML-Fragment soll das entsprechende Element mit der ID "sum"
der Seite im Browser ersetzen. "oob"
bedeutet out-of-band.
Mein nächstes Ziel war es, ein reproduzierbares Beispiel zu erstellen, damit ich das Problem teilen kann.
Stackoverflow hat eine Funktion, die ein bisschen wie jsfiddle ist. Man kann HTML/CSS/JS-Code eingeben und ihn ausführen. Das funktioniert für die meisten Anwendungsfälle gut. In meinem Anwendungsfall benötige ich eine HTTP-Antwort wie oben. Wie erstellt man sie?
Die freundlichen Jungs vom htmx discord channel haben mir einen Tipp gegeben. Es gibt einen coolen kostenlosen Dienst, mit dem man HTML-Endpunkte erstellen kann, die beliebige HTTP-Antworten erzeugen: https://designer.mocky.io/. Mit Mocky können Sie HTTP-GET- oder POST-Antworten erstellen. Sie können den Inhalt, den Inhaltstyp und die HTTP-Header konfigurieren. Mit Mocky und der einfachen jsfiddle-Funktion von Stackoverflow konnte ich ein reproduzierbares Beispiel erstellen:
https://stackoverflow.com/questions/67289814/htmx-e-queryselectorall-is-not-a-function
Erkenntnis:
Natürlich hatte niemand eine Antwort auf meine Frage. Das hatte ich erwartet. Ein paar Tage später hatte ich Zeit, die Sache ein wenig zu vertiefen. Mein Beispiel verwendete die Minified-Version der Bibliothek, was bedeutete, dass der Code nicht leicht zu verstehen war. Nachdem ich auf die nicht-minifizierte Bibliothek umgestiegen war, erhielt ich eine Stacktrace, der verständlicher war:
Uncaught TypeError:eltOrSelector.querySelectorAll is not a function
at findAll (htmx.js:295)
at handleOutOfBandSwaps (htmx.js:501)
at selectAndSwap (htmx.js:712)
at doSwap (htmx.js:2284)
at handleAjaxResponse (htmx.js:2358)
at XMLHttpRequest.xhr.onload (htmx.js:2163)
ach einigem Herumstochern habe ich herausgefunden, dass die Methode makeFragment() von htmx parseFromString()
verwendet. Diese Methode funktioniert gut, aber nicht für meine Eingabe:
Im obigen Screenshot von Chromium's Devtools sehen Sie, dass <tr>…</tr>
verworfen wurde.
Jetzt stoßen wir auf einen sehr grundlegenden Teil der "html-Fragmente über die Leitung". Die Methode DOMParser.parseFromString()
validiert das HTML und meine Eingabe ist kein gültiges HTML, da ein <tr>
-Tag unter einem <table>
-Tag stehen muss. Bisher hat HTMX das erste Tag des HTML-Fragments betrachtet und das gesamte Fragment entsprechend umschlossen. Das funktioniert in den meisten Fällen, schlägt aber fehl, wenn das Out-of-Band-Element von einem anderen Typ ist.
Da dies ein Hobbyprojekt ist, programmiere ich meistens am Wochenende. Das hat den Vorteil, dass man zwischen den einzelnen Programmiersitzungen mehrere Tage Zeit hat, sodass man einen frischen Blick auf die Dinge hat, wenn man wieder anfängt.
Die Lösung des Problems ist also offensichtlich. Wenn ich den gleichen Elementtyp für die Out-of-Band-Daten verwende, dann funktioniert das Wrapping, das von htmx durchgeführt wird. Diese html-Fragmente können von htmx geparst werden:
<tr>
<td>2</td><td>two</td>
</tr>
<tr id=”sum” hx-swap-oob=”true”>
<td>MAGIC</td>
</tr>
Ich muss nur die gleiche Art von Tag im zweiten Tag verwenden.
Erkenntnisse:
Ich war froh, dass ich eine Lösung gefunden habe. Neue Entwickler, die auf dieses Problem stoßen, werden bald eine Antwort finden, wenn sie die Fehlermeldung in ihre Lieblingssuchmaschine eingeben. Das gibt mir ein gutes Gefühl, weil ich etwas erreicht habe, das anderen Menschen hilft.
Langfristig wäre es schön, wenn htmx jedes Html-Fragment parsen könnte. Anstelle der derzeitigen Implementierung. Vielleicht könnte es eine Lösung sein, das Fragment in einen <template>
-Tag zu packen. Aber das ist eine andere Frage, der ich später nachgehen werde. Siehe Issue #469.
Erkenntnisse: