Computerhaus Quickborn

Tutorial: Serverseitiges Rendering mit Astro & HTMX​

loading="lazy" width="400px">TippaPatt – shutterstock.com Astro ist in erster Linie als aufstrebendes JavaScript-Meta-Framework bekannt, das reaktive Frontends wie React und Vue als Plugins unterstützt. Inzwischen entwickelt sich Astro jedoch mehr und mehr zu einer Standalone-Backend-Lösung, die mit Endpoints und Routing so gut wie jeden Task bewältigt, den man auf sie loslässt. In diesem Tutorial werden wir Astro auf Herz und Nieren prüfen, indem wir es dazu nutzen, mit HTMX Views zu hosten. Interessant, respektive herausfordernd ist dabei, dass wir im Rahmen der Antworten View Fragments senden.   Sämtlichen Demo-Code, der in diesem Tutorial besprochen wird, finden Sie hier.  Erste Schritte Im ersten Schritt starten wir eine neue Applikation mit dem standardmäßigen Befehlszeilen-Tool von Astro (mehr Infos zum Astro CLI finden Sie in der Dokumentation): $ npm create astro@latest Weil wir für unser Beispiel dynamische Endpunkte nutzen werden, muss Astro „wissen“, welche Art von Deployment-Adapter genutzt wird. In unserem Fall nutzen wir den für die Node-Integration: $ npx astro add node Der Service Layer Jetzt können wir damit loslegen, benutzerdefinierten Code auf Ebene des Service Layer zu schreiben. Letztgenannter stellt einen zentralen Ort für sämtliche Middleware zur Verfügung, die in der gesamten Anwendung wiederverwendet werden kann. Im Fall einer echten Applikation würde der Service Layer über einen Data Layer mit einem Data Store interagieren. Für unser Beispiel greifen wir der Einfachheit halber auf In-Memory-Daten zurück. Für diese Art von Dingen ist es in Astro Konvention, ein /lib-Verzeichnis zu verwenden. Sämtlicher Code für Astro wird hingegen im /src-Verzeichnis abgelegt – unser Service-Code entsprechend in src/ib/todo.js: src/lib/todo.js: // src/lib/todo.js let todosData = [ { id: 1, text: "Learn Kung Fu", completed: false }, { id: 2, text: "Watch Westminster", completed: true }, { id: 3, text: "Study Vedanta", completed: false }, ]; export async function getTodos() { return new Promise((resolve) => { setTimeout(() => { resolve(todosData); }, 100); // Simulate a slight delay for fetching }); } export async function deleteTodo(id) { return new Promise((resolve) => { setTimeout(() => { todosData = todosData.filter(todo => todo.id !== id); resolve(true); }, 100); }); } export async function addTodo(text) { return new Promise((resolve) => { setTimeout(() => { const newTodo = { id: todosData.length+1, text, completed: false }; todosData.push(newTodo); resolve(newTodo); }, 100); }); } Allgemein ist zu beachten, dass sämtliche unserer Funktionen Promises zurückgeben. Das unterstützt Astro standardmäßig – was hervorragend ist, wenn diese Service-Methoden mit einem Data Store kommunizieren müssen. In unserem Fall simulieren wir eine Netzwerkverzögerung mit einem Timeout. Ansonsten handelt es sich um Vanilla-JavaScript-Calls, die einige einfache, funktionale Operatoren einsetzen, um das In-Memory-Array von todosData zu verarbeiten.   Die Hauptansicht Werfen wir nun einen Blick auf src/pages/index.astro. Der Befehl astro create hat hier eine einfache Welcome Page erstellt, die wir für unsere Zwecke umfunktionieren können. Dazu löschen wir im ersten Schritt alle Verweise auf die Welcome-Komponente. Stattdessen nutzen wir unsere eigene TodoList-Komponente: ---- // src/pages/index.astro import Layout from '../layouts/Layout.astro'; import TodoList from '../components/TodoList.astro'; --- In Astro-Komponenten (definiert in .astro-Dateien) gibt es zwei Segmente: das JavaScript innerhalb der „Code-Klammern“ (----) und das HTML-basierte Template. Das ist ganz ähnlich wie bei anderen Templating-Technologien. Allerdings hebt sich Astro dadurch ab, dass standardmäßig alles auf dem Server ausgeführt, in einem HTML-Paket mit minimalem JavaScript gebündelt und an den Client gesendet wird. Reusable Components Sehen wir uns nun die TodoList-Komponente an. Das Komponentenverzeichnis enthält sämtliche wiederverwendbaren .astro-Komponenten. TodoList ist entsprechend in src/components/TodoList.astro verortet: --- // src/components/TodoList.astro import { getTodos, deleteTodo } from '../lib/todo'; import TodoItem from './TodoItem.astro'; const todos = await getTodos(); --- Add Todo {todos.map(todo => ( ))} TodoList importiert die erforderlichen Funktionen aus dem eben gesehenen Servicemodul und nutzt diese, um eine Ansicht zu rendern. Um die To-Dos abzurufen, nutzt es zunächst await – bevor es dann innerhalb des Templates mit todos.map Loops über diese schreibt. Dabei nutzen wir für jedes Todo die TodoItem-Komponente und übergeben eine Property, die die To-Do-Daten enthält. Darüber hinaus kommt ein Formular zum Einsatz, um neue To-Dos zu erstellen. Dieses mit AJAX-Background auszustatten und einen Reload der Seite zu verhindern, übernimmt HTMX. Dabei: sendet hx-post einen POST-Request an /api/todos. gibt hx-target an, wo die Antwort (im To-Do-Element) abgelegt werden soll. legt hx-swap fest, wie das neue Element hinzugefügt werden soll (am Ende der Liste). Dieser Teil der Benutzeroberfläche wird vorab auf dem Server gerendert und vorkonfiguriert an den Browser gesendet. Die TodoItem-Komponente Bevor wir uns die API ansehen, die die Create Requests verarbeitet, werfen wir einen Blick auf die TodoItem-Komponente unter src/components/TodoItem.astro: --- // src/components/TodoItem.astro import { deleteTodo, getTodos } from '../services/todo'; export interface Props { todo: { id: number; text: string; completed: boolean }; } const { todo } = Astro.props; --- {todo.text} {todo.completed ? ' (Completed)' : ''} Delete Das TodoItem akzeptiert die zuvor gesehenen Properties. Mit diesen erstellen wir ein simples Listenelement für todo sowie eine Delete-Schaltfläche – die HTMX nutzt, um diese Aufgabe über AJAX zu erledigen). In diesem Fall kommt hx-delete zum Einsatz, was einen DELETE-Request auslöst. Die Attribute hx-target und hx-swap demonstrieren, wie leistungsfähig HTMX ist, wenn es um diese einfachen Eigenschaften geht: Um es zu löschen, wählen wir das Listenelement einfach selbst aus (hx-delete entfernt standardmäßig sein Target-Element, wenn es eine HTTP-Erfolgsmeldung erhält).  Endpunkt löschen Im nächsten Schritt schauen wir uns an, wie der DELETE-Request verarbeitet werden kann. Dank Astros Datei-basiertem Routing lassen sich unsere Server-Endpunkte innerhalb von /pages definieren. Dabei kommt dieselbe Routing-Semantik zum Einsatz. Für eine bessere Organisation erstellen wir ein /api-Unterverzeichnis und nutzen einen route-Parameter, um die todo-ID zu erfassen, die zu Löschzwecken übermittelt wurde. Dadurch erhalten wir die Datei src/pages/api/todos/[id].js: import { deleteTodo, getTodos } from '../../../lib/todo'; export const prerender = false; export async function DELETE({ params, request }) { const id = parseInt(params.id, 10); if (isNaN(id)) { return new Response(null, { status: 400, statusText: 'Invalid ID' }); } await deleteTodo(id); return new Response(null, { status: 200 }); // Empty response is sufficient for delete } Astro-Endpunkte sind im Grunde Views – bis auf den Template-Part. Ein wichtiger allgemeiner Hinweis: Wir verwenden das Flag prerender = false, um sicherzustellen, dass die Engine in der Build-Phase nicht versucht, diesen Endpunkt zu erstellen. Der Endpunkt wird als DELETE bezeichnet und verwendet die Servicefunktion, um mit den Daten zu arbeiten. Er gibt ein leeres Response-Objekt mit einem 200-Erfolgscode zurück. Wenn HTMX diesen im Frontend erhält, wird das Element aus der Ansicht entfernt. Der Todo-Creation-Endpunkt Das letzte große Puzzleteil: den Todo-Creation-Endpunkt zu händeln. Dazu müssen wir den Text des neuen Todo akzeptieren, ihn zur Liste hinzufügen und das einzufügende Markup zurücksenden. In den meisten Fällen würde das über einen anderen Server-Endpunkt laufen. Allerdings ist das Feature, Komponenten programmgesteuert zu rendern, bei Astro noch in der Entwicklung. Vorläufig gibt es dafür einen relativ einfachen Workaround: Den Request über Page View händeln und die TodoItem.astro-Komponente wiederverwenden, um ein Antwortfragment zu senden. Folgendermaßen sieht unser „Pseudo-Endpunkt“ in src/pages/api/todos/index.astro aus: --- import {addTodo} from '../../../lib/todo.js'; import TodoItem from '../../../components/TodoItem.astro'; let newTodo = null; if (Astro.request.method === 'POST') { const formData = await Astro.request.formData(); newTodo = await addTodo(formData.get('text')); } export const prerender = false; --- Sie werden feststellen, dass das JavaScript vollen Zugriff auf den Request hat. Wir können also problemlos nach Methodentyp filtern. Ganz generell handelt es sich bei dem JavaScript um eine vollumfängliche serverseitige Funktion. Die Hauptarbeit besteht darin, den Text des Anfrageformulars mithilfe der create-Funktion unserer Service Utility in ein neues To-Do-Element umzuwandeln. Dieses nutzen wir anschließend als Property für die TodoItem-Komponente. App ausführen Um die App im Entwicklungsmodus auszuführen, nutzen Sie: $ npx astro dev Um einen Production Build (Output nach /dist) zu erstellen, geben Sie folgenden Befehl ein: $ npx astro build Atsro.js: Development-Experience-Eindrücke Mit Astro zu arbeiten, bietet ein gutes Erlebnis: Sein Dev-Modus ist schnell und ziemlich stabil – und das Framework performt sehr gut, wenn es darum geht, nur modifizierte Chunks neu zu laden. Zudem werden hilfreiche, relevante (Hot)Links zu Fehlern im Browser angezeigt – wie etwa im nachfolgenden Screenshot zu sehen. Matthew Tyson Diese Art des Fehler-Reportings demonstriert, dass die Developer Experience bei diesem Projekt im Fokus steht. (fm) Sie wollen weitere interessante Beiträge zu diversen Themen aus der IT-Welt lesen? Unsere kostenlosen Newsletter liefern Ihnen alles, was IT-Profis wissen sollten – direkt in Ihre Inbox! 

Tutorial: Serverseitiges Rendering mit Astro & HTMX​ loading="lazy" width="400px">TippaPatt – shutterstock.com Astro ist in erster Linie als aufstrebendes JavaScript-Meta-Framework bekannt, das reaktive Frontends wie React und Vue als Plugins unterstützt. Inzwischen entwickelt sich Astro jedoch mehr und mehr zu einer Standalone-Backend-Lösung, die mit Endpoints und Routing so gut wie jeden Task bewältigt, den man auf sie loslässt. In diesem Tutorial werden wir Astro auf Herz und Nieren prüfen, indem wir es dazu nutzen, mit HTMX Views zu hosten. Interessant, respektive herausfordernd ist dabei, dass wir im Rahmen der Antworten View Fragments senden.   Sämtlichen Demo-Code, der in diesem Tutorial besprochen wird, finden Sie hier.  Erste Schritte Im ersten Schritt starten wir eine neue Applikation mit dem standardmäßigen Befehlszeilen-Tool von Astro (mehr Infos zum Astro CLI finden Sie in der Dokumentation): $ npm create astro@latest Weil wir für unser Beispiel dynamische Endpunkte nutzen werden, muss Astro „wissen“, welche Art von Deployment-Adapter genutzt wird. In unserem Fall nutzen wir den für die Node-Integration: $ npx astro add node Der Service Layer Jetzt können wir damit loslegen, benutzerdefinierten Code auf Ebene des Service Layer zu schreiben. Letztgenannter stellt einen zentralen Ort für sämtliche Middleware zur Verfügung, die in der gesamten Anwendung wiederverwendet werden kann. Im Fall einer echten Applikation würde der Service Layer über einen Data Layer mit einem Data Store interagieren. Für unser Beispiel greifen wir der Einfachheit halber auf In-Memory-Daten zurück. Für diese Art von Dingen ist es in Astro Konvention, ein /lib-Verzeichnis zu verwenden. Sämtlicher Code für Astro wird hingegen im /src-Verzeichnis abgelegt – unser Service-Code entsprechend in src/ib/todo.js: src/lib/todo.js: // src/lib/todo.js let todosData = [ { id: 1, text: "Learn Kung Fu", completed: false }, { id: 2, text: "Watch Westminster", completed: true }, { id: 3, text: "Study Vedanta", completed: false }, ]; export async function getTodos() { return new Promise((resolve) => { setTimeout(() => { resolve(todosData); }, 100); // Simulate a slight delay for fetching }); } export async function deleteTodo(id) { return new Promise((resolve) => { setTimeout(() => { todosData = todosData.filter(todo => todo.id !== id); resolve(true); }, 100); }); } export async function addTodo(text) { return new Promise((resolve) => { setTimeout(() => { const newTodo = { id: todosData.length+1, text, completed: false }; todosData.push(newTodo); resolve(newTodo); }, 100); }); } Allgemein ist zu beachten, dass sämtliche unserer Funktionen Promises zurückgeben. Das unterstützt Astro standardmäßig – was hervorragend ist, wenn diese Service-Methoden mit einem Data Store kommunizieren müssen. In unserem Fall simulieren wir eine Netzwerkverzögerung mit einem Timeout. Ansonsten handelt es sich um Vanilla-JavaScript-Calls, die einige einfache, funktionale Operatoren einsetzen, um das In-Memory-Array von todosData zu verarbeiten.   Die Hauptansicht Werfen wir nun einen Blick auf src/pages/index.astro. Der Befehl astro create hat hier eine einfache Welcome Page erstellt, die wir für unsere Zwecke umfunktionieren können. Dazu löschen wir im ersten Schritt alle Verweise auf die Welcome-Komponente. Stattdessen nutzen wir unsere eigene TodoList-Komponente: ---- // src/pages/index.astro import Layout from '../layouts/Layout.astro'; import TodoList from '../components/TodoList.astro'; --- In Astro-Komponenten (definiert in .astro-Dateien) gibt es zwei Segmente: das JavaScript innerhalb der „Code-Klammern“ (----) und das HTML-basierte Template. Das ist ganz ähnlich wie bei anderen Templating-Technologien. Allerdings hebt sich Astro dadurch ab, dass standardmäßig alles auf dem Server ausgeführt, in einem HTML-Paket mit minimalem JavaScript gebündelt und an den Client gesendet wird. Reusable Components Sehen wir uns nun die TodoList-Komponente an. Das Komponentenverzeichnis enthält sämtliche wiederverwendbaren .astro-Komponenten. TodoList ist entsprechend in src/components/TodoList.astro verortet: --- // src/components/TodoList.astro import { getTodos, deleteTodo } from '../lib/todo'; import TodoItem from './TodoItem.astro'; const todos = await getTodos(); --- Add Todo {todos.map(todo => ( ))} TodoList importiert die erforderlichen Funktionen aus dem eben gesehenen Servicemodul und nutzt diese, um eine Ansicht zu rendern. Um die To-Dos abzurufen, nutzt es zunächst await – bevor es dann innerhalb des Templates mit todos.map Loops über diese schreibt. Dabei nutzen wir für jedes Todo die TodoItem-Komponente und übergeben eine Property, die die To-Do-Daten enthält. Darüber hinaus kommt ein Formular zum Einsatz, um neue To-Dos zu erstellen. Dieses mit AJAX-Background auszustatten und einen Reload der Seite zu verhindern, übernimmt HTMX. Dabei: sendet hx-post einen POST-Request an /api/todos. gibt hx-target an, wo die Antwort (im To-Do-Element) abgelegt werden soll. legt hx-swap fest, wie das neue Element hinzugefügt werden soll (am Ende der Liste). Dieser Teil der Benutzeroberfläche wird vorab auf dem Server gerendert und vorkonfiguriert an den Browser gesendet. Die TodoItem-Komponente Bevor wir uns die API ansehen, die die Create Requests verarbeitet, werfen wir einen Blick auf die TodoItem-Komponente unter src/components/TodoItem.astro: --- // src/components/TodoItem.astro import { deleteTodo, getTodos } from '../services/todo'; export interface Props { todo: { id: number; text: string; completed: boolean }; } const { todo } = Astro.props; --- {todo.text} {todo.completed ? ' (Completed)' : ''} Delete Das TodoItem akzeptiert die zuvor gesehenen Properties. Mit diesen erstellen wir ein simples Listenelement für todo sowie eine Delete-Schaltfläche – die HTMX nutzt, um diese Aufgabe über AJAX zu erledigen). In diesem Fall kommt hx-delete zum Einsatz, was einen DELETE-Request auslöst. Die Attribute hx-target und hx-swap demonstrieren, wie leistungsfähig HTMX ist, wenn es um diese einfachen Eigenschaften geht: Um es zu löschen, wählen wir das Listenelement einfach selbst aus (hx-delete entfernt standardmäßig sein Target-Element, wenn es eine HTTP-Erfolgsmeldung erhält).  Endpunkt löschen Im nächsten Schritt schauen wir uns an, wie der DELETE-Request verarbeitet werden kann. Dank Astros Datei-basiertem Routing lassen sich unsere Server-Endpunkte innerhalb von /pages definieren. Dabei kommt dieselbe Routing-Semantik zum Einsatz. Für eine bessere Organisation erstellen wir ein /api-Unterverzeichnis und nutzen einen route-Parameter, um die todo-ID zu erfassen, die zu Löschzwecken übermittelt wurde. Dadurch erhalten wir die Datei src/pages/api/todos/[id].js: import { deleteTodo, getTodos } from '../../../lib/todo'; export const prerender = false; export async function DELETE({ params, request }) { const id = parseInt(params.id, 10); if (isNaN(id)) { return new Response(null, { status: 400, statusText: 'Invalid ID' }); } await deleteTodo(id); return new Response(null, { status: 200 }); // Empty response is sufficient for delete } Astro-Endpunkte sind im Grunde Views – bis auf den Template-Part. Ein wichtiger allgemeiner Hinweis: Wir verwenden das Flag prerender = false, um sicherzustellen, dass die Engine in der Build-Phase nicht versucht, diesen Endpunkt zu erstellen. Der Endpunkt wird als DELETE bezeichnet und verwendet die Servicefunktion, um mit den Daten zu arbeiten. Er gibt ein leeres Response-Objekt mit einem 200-Erfolgscode zurück. Wenn HTMX diesen im Frontend erhält, wird das Element aus der Ansicht entfernt. Der Todo-Creation-Endpunkt Das letzte große Puzzleteil: den Todo-Creation-Endpunkt zu händeln. Dazu müssen wir den Text des neuen Todo akzeptieren, ihn zur Liste hinzufügen und das einzufügende Markup zurücksenden. In den meisten Fällen würde das über einen anderen Server-Endpunkt laufen. Allerdings ist das Feature, Komponenten programmgesteuert zu rendern, bei Astro noch in der Entwicklung. Vorläufig gibt es dafür einen relativ einfachen Workaround: Den Request über Page View händeln und die TodoItem.astro-Komponente wiederverwenden, um ein Antwortfragment zu senden. Folgendermaßen sieht unser „Pseudo-Endpunkt“ in src/pages/api/todos/index.astro aus: --- import {addTodo} from '../../../lib/todo.js'; import TodoItem from '../../../components/TodoItem.astro'; let newTodo = null; if (Astro.request.method === 'POST') { const formData = await Astro.request.formData(); newTodo = await addTodo(formData.get('text')); } export const prerender = false; --- Sie werden feststellen, dass das JavaScript vollen Zugriff auf den Request hat. Wir können also problemlos nach Methodentyp filtern. Ganz generell handelt es sich bei dem JavaScript um eine vollumfängliche serverseitige Funktion. Die Hauptarbeit besteht darin, den Text des Anfrageformulars mithilfe der create-Funktion unserer Service Utility in ein neues To-Do-Element umzuwandeln. Dieses nutzen wir anschließend als Property für die TodoItem-Komponente. App ausführen Um die App im Entwicklungsmodus auszuführen, nutzen Sie: $ npx astro dev Um einen Production Build (Output nach /dist) zu erstellen, geben Sie folgenden Befehl ein: $ npx astro build Atsro.js: Development-Experience-Eindrücke Mit Astro zu arbeiten, bietet ein gutes Erlebnis: Sein Dev-Modus ist schnell und ziemlich stabil – und das Framework performt sehr gut, wenn es darum geht, nur modifizierte Chunks neu zu laden. Zudem werden hilfreiche, relevante (Hot)Links zu Fehlern im Browser angezeigt – wie etwa im nachfolgenden Screenshot zu sehen. Matthew Tyson Diese Art des Fehler-Reportings demonstriert, dass die Developer Experience bei diesem Projekt im Fokus steht. (fm) Sie wollen weitere interessante Beiträge zu diversen Themen aus der IT-Welt lesen? Unsere kostenlosen Newsletter liefern Ihnen alles, was IT-Profis wissen sollten – direkt in Ihre Inbox!

loading=”lazy” width=”400px”>TippaPatt – shutterstock.com Astro ist in erster Linie als aufstrebendes JavaScript-Meta-Framework bekannt, das reaktive Frontends wie React und Vue als Plugins unterstützt. Inzwischen entwickelt sich Astro jedoch mehr und mehr zu einer Standalone-Backend-Lösung, die mit Endpoints und Routing so gut wie jeden Task bewältigt, den man auf sie loslässt. In diesem Tutorial werden wir Astro auf Herz und Nieren prüfen, indem wir es dazu nutzen, mit HTMX Views zu hosten. Interessant, respektive herausfordernd ist dabei, dass wir im Rahmen der Antworten View Fragments senden.   Sämtlichen Demo-Code, der in diesem Tutorial besprochen wird, finden Sie hier.  Erste Schritte Im ersten Schritt starten wir eine neue Applikation mit dem standardmäßigen Befehlszeilen-Tool von Astro (mehr Infos zum Astro CLI finden Sie in der Dokumentation): $ npm create astro@latest Weil wir für unser Beispiel dynamische Endpunkte nutzen werden, muss Astro „wissen“, welche Art von Deployment-Adapter genutzt wird. In unserem Fall nutzen wir den für die Node-Integration: $ npx astro add node Der Service Layer Jetzt können wir damit loslegen, benutzerdefinierten Code auf Ebene des Service Layer zu schreiben. Letztgenannter stellt einen zentralen Ort für sämtliche Middleware zur Verfügung, die in der gesamten Anwendung wiederverwendet werden kann. Im Fall einer echten Applikation würde der Service Layer über einen Data Layer mit einem Data Store interagieren. Für unser Beispiel greifen wir der Einfachheit halber auf In-Memory-Daten zurück. Für diese Art von Dingen ist es in Astro Konvention, ein /lib-Verzeichnis zu verwenden. Sämtlicher Code für Astro wird hingegen im /src-Verzeichnis abgelegt – unser Service-Code entsprechend in src/ib/todo.js: src/lib/todo.js: // src/lib/todo.js let todosData = [ { id: 1, text: “Learn Kung Fu”, completed: false }, { id: 2, text: “Watch Westminster”, completed: true }, { id: 3, text: “Study Vedanta”, completed: false }, ]; export async function getTodos() { return new Promise((resolve) => { setTimeout(() => { resolve(todosData); }, 100); // Simulate a slight delay for fetching }); } export async function deleteTodo(id) { return new Promise((resolve) => { setTimeout(() => { todosData = todosData.filter(todo => todo.id !== id); resolve(true); }, 100); }); } export async function addTodo(text) { return new Promise((resolve) => { setTimeout(() => { const newTodo = { id: todosData.length+1, text, completed: false }; todosData.push(newTodo); resolve(newTodo); }, 100); }); } Allgemein ist zu beachten, dass sämtliche unserer Funktionen Promises zurückgeben. Das unterstützt Astro standardmäßig – was hervorragend ist, wenn diese Service-Methoden mit einem Data Store kommunizieren müssen. In unserem Fall simulieren wir eine Netzwerkverzögerung mit einem Timeout. Ansonsten handelt es sich um Vanilla-JavaScript-Calls, die einige einfache, funktionale Operatoren einsetzen, um das In-Memory-Array von todosData zu verarbeiten.   Die Hauptansicht Werfen wir nun einen Blick auf src/pages/index.astro. Der Befehl astro create hat hier eine einfache Welcome Page erstellt, die wir für unsere Zwecke umfunktionieren können. Dazu löschen wir im ersten Schritt alle Verweise auf die Welcome-Komponente. Stattdessen nutzen wir unsere eigene TodoList-Komponente: —- // src/pages/index.astro import Layout from ‘../layouts/Layout.astro’; import TodoList from ‘../components/TodoList.astro’; — In Astro-Komponenten (definiert in .astro-Dateien) gibt es zwei Segmente: das JavaScript innerhalb der „Code-Klammern“ (—-) und das HTML-basierte Template. Das ist ganz ähnlich wie bei anderen Templating-Technologien. Allerdings hebt sich Astro dadurch ab, dass standardmäßig alles auf dem Server ausgeführt, in einem HTML-Paket mit minimalem JavaScript gebündelt und an den Client gesendet wird. Reusable Components Sehen wir uns nun die TodoList-Komponente an. Das Komponentenverzeichnis enthält sämtliche wiederverwendbaren .astro-Komponenten. TodoList ist entsprechend in src/components/TodoList.astro verortet: — // src/components/TodoList.astro import { getTodos, deleteTodo } from ‘../lib/todo’; import TodoItem from ‘./TodoItem.astro’; const todos = await getTodos(); — Add Todo {todos.map(todo => ( ))} TodoList importiert die erforderlichen Funktionen aus dem eben gesehenen Servicemodul und nutzt diese, um eine Ansicht zu rendern. Um die To-Dos abzurufen, nutzt es zunächst await – bevor es dann innerhalb des Templates mit todos.map Loops über diese schreibt. Dabei nutzen wir für jedes Todo die TodoItem-Komponente und übergeben eine Property, die die To-Do-Daten enthält. Darüber hinaus kommt ein Formular zum Einsatz, um neue To-Dos zu erstellen. Dieses mit AJAX-Background auszustatten und einen Reload der Seite zu verhindern, übernimmt HTMX. Dabei: sendet hx-post einen POST-Request an /api/todos. gibt hx-target an, wo die Antwort (im To-Do-Element) abgelegt werden soll. legt hx-swap fest, wie das neue Element hinzugefügt werden soll (am Ende der Liste). Dieser Teil der Benutzeroberfläche wird vorab auf dem Server gerendert und vorkonfiguriert an den Browser gesendet. Die TodoItem-Komponente Bevor wir uns die API ansehen, die die Create Requests verarbeitet, werfen wir einen Blick auf die TodoItem-Komponente unter src/components/TodoItem.astro: — // src/components/TodoItem.astro import { deleteTodo, getTodos } from ‘../services/todo’; export interface Props { todo: { id: number; text: string; completed: boolean }; } const { todo } = Astro.props; — {todo.text} {todo.completed ? ‘ (Completed)’ : ”} Delete Das TodoItem akzeptiert die zuvor gesehenen Properties. Mit diesen erstellen wir ein simples Listenelement für todo sowie eine Delete-Schaltfläche – die HTMX nutzt, um diese Aufgabe über AJAX zu erledigen). In diesem Fall kommt hx-delete zum Einsatz, was einen DELETE-Request auslöst. Die Attribute hx-target und hx-swap demonstrieren, wie leistungsfähig HTMX ist, wenn es um diese einfachen Eigenschaften geht: Um es zu löschen, wählen wir das Listenelement einfach selbst aus (hx-delete entfernt standardmäßig sein Target-Element, wenn es eine HTTP-Erfolgsmeldung erhält).  Endpunkt löschen Im nächsten Schritt schauen wir uns an, wie der DELETE-Request verarbeitet werden kann. Dank Astros Datei-basiertem Routing lassen sich unsere Server-Endpunkte innerhalb von /pages definieren. Dabei kommt dieselbe Routing-Semantik zum Einsatz. Für eine bessere Organisation erstellen wir ein /api-Unterverzeichnis und nutzen einen route-Parameter, um die todo-ID zu erfassen, die zu Löschzwecken übermittelt wurde. Dadurch erhalten wir die Datei src/pages/api/todos/[id].js: import { deleteTodo, getTodos } from ‘../../../lib/todo’; export const prerender = false; export async function DELETE({ params, request }) { const id = parseInt(params.id, 10); if (isNaN(id)) { return new Response(null, { status: 400, statusText: ‘Invalid ID’ }); } await deleteTodo(id); return new Response(null, { status: 200 }); // Empty response is sufficient for delete } Astro-Endpunkte sind im Grunde Views – bis auf den Template-Part. Ein wichtiger allgemeiner Hinweis: Wir verwenden das Flag prerender = false, um sicherzustellen, dass die Engine in der Build-Phase nicht versucht, diesen Endpunkt zu erstellen. Der Endpunkt wird als DELETE bezeichnet und verwendet die Servicefunktion, um mit den Daten zu arbeiten. Er gibt ein leeres Response-Objekt mit einem 200-Erfolgscode zurück. Wenn HTMX diesen im Frontend erhält, wird das Element aus der Ansicht entfernt. Der Todo-Creation-Endpunkt Das letzte große Puzzleteil: den Todo-Creation-Endpunkt zu händeln. Dazu müssen wir den Text des neuen Todo akzeptieren, ihn zur Liste hinzufügen und das einzufügende Markup zurücksenden. In den meisten Fällen würde das über einen anderen Server-Endpunkt laufen. Allerdings ist das Feature, Komponenten programmgesteuert zu rendern, bei Astro noch in der Entwicklung. Vorläufig gibt es dafür einen relativ einfachen Workaround: Den Request über Page View händeln und die TodoItem.astro-Komponente wiederverwenden, um ein Antwortfragment zu senden. Folgendermaßen sieht unser „Pseudo-Endpunkt“ in src/pages/api/todos/index.astro aus: — import {addTodo} from ‘../../../lib/todo.js’; import TodoItem from ‘../../../components/TodoItem.astro’; let newTodo = null; if (Astro.request.method === ‘POST’) { const formData = await Astro.request.formData(); newTodo = await addTodo(formData.get(‘text’)); } export const prerender = false; — Sie werden feststellen, dass das JavaScript vollen Zugriff auf den Request hat. Wir können also problemlos nach Methodentyp filtern. Ganz generell handelt es sich bei dem JavaScript um eine vollumfängliche serverseitige Funktion. Die Hauptarbeit besteht darin, den Text des Anfrageformulars mithilfe der create-Funktion unserer Service Utility in ein neues To-Do-Element umzuwandeln. Dieses nutzen wir anschließend als Property für die TodoItem-Komponente. App ausführen Um die App im Entwicklungsmodus auszuführen, nutzen Sie: $ npx astro dev Um einen Production Build (Output nach /dist) zu erstellen, geben Sie folgenden Befehl ein: $ npx astro build Atsro.js: Development-Experience-Eindrücke Mit Astro zu arbeiten, bietet ein gutes Erlebnis: Sein Dev-Modus ist schnell und ziemlich stabil – und das Framework performt sehr gut, wenn es darum geht, nur modifizierte Chunks neu zu laden. Zudem werden hilfreiche, relevante (Hot)Links zu Fehlern im Browser angezeigt – wie etwa im nachfolgenden Screenshot zu sehen. Matthew Tyson Diese Art des Fehler-Reportings demonstriert, dass die Developer Experience bei diesem Projekt im Fokus steht. (fm) Sie wollen weitere interessante Beiträge zu diversen Themen aus der IT-Welt lesen? Unsere kostenlosen Newsletter liefern Ihnen alles, was IT-Profis wissen sollten – direkt in Ihre Inbox! 

Nach oben scrollen
×