Die Texte in diesem Artikel wurden teilweise mit Hilfe künstlicher Intelligenz erarbeitet und von uns korrigiert und überarbeitet. Für die Generierung wurden folgende Dienste verwendet:
Wie wir maschinelles Lernen bei der Erstellung unserer Artikel einsetzenWenn du noch nicht mit der Arbeit an Formularen mit Alpine.js vertraut bist, kannst du deine Kenntnisse in unserem ersten Artikel zu diesem Thema, Interaktive Formulare mit Alpine.js, auffrischen.
In unserem ersten Artikel zu interaktiven Formularen mit Alpine.js haben wir bereits angedeutet, dass Alpine.js neben der allgemeinen Darstellung von serverseitigen Informationen im Formular auch dazu verwendet werden kann, um u.a. Einfluss auf einzelne Elemente zu nehmen.
Aufgrund der großen Nachfrage haben wir uns entschlossen, genau dieses Thema in diesem Folgeartikel aufzugreifen und beispielhaft zu zeigen, wie man mit Alpine.js Informationen und Zustände zur Validierung eines Formulars nutzen kann.
Einrichtung
Für diese Demonstration verwenden wir unsere Astro Boilerplate, die wir bereits in einem früheren Artikel ausführlich vorgestellt haben.
Download Astro BoilerplateWenn unsere Boilerplate nicht das Richtige für dich ist, ist das kein Problem. Die Schritte zur Validierung von Formulareingaben funktionieren in jedem Projekt mit Alpine.js.
Methoden für Alpine.js integrieren
Um im weiteren Verlauf der Implementierung auf die benötigten Daten und Methoden aus Alpine.js zugreifen zu können, werden diese zunächst deklariert, um im weiteren Verlauf Fehler zu vermeiden.
form.ts
form()
kontrolliert den Zustand loading
und speichert die vom Server gesendete Response
über die Methode submit()
, die beim Absenden des Formulars ausgeführt wird.
Außerdem ist eine fiktive fakeResponse()
enthalten, die beispielhafte und vereinfachte Validierungsfehler von unserem fiktiven Backend “empfängt”.
import { sleep } from "../utilities"
export const form = () => ({
loading: false,
response: null as unknown,
async submit(event: SubmitEvent) {
this.loading = true
this.response = null
const formData = new FormData(event.target as HTMLFormElement)
/**
* Replace the following fake response with your `fetch` request and
* receive the validated results from the server side as JSON.
*
* Make sure you add the necessary attributes to the `<Input />'
* elements to perform client-side validation as well.
*/
const fakeResponse = async () => {
await sleep(1000) // Mock response time
return {
errors: {
// [input.name]: "message string"
username: "Username is alrady taken",
password: "Password is too short"
}
}
}
this.response = await fakeResponse()
this.loading = false
}
})
Die Response
muss ein error
Objekt enthalten, in dem jedes Schlüssel-Wert-Paar aus dem Namen des Eingabeelements und dem zugehörigen Validierungsfehler besteht.
input.ts
input.ts
übernimmt die Darstellung der Validierungsfehler für ein Eingabeelement über die Methode validate()
, die über das Attribut x-effect
eingebunden wird, um die Daten für die Darstellung beim Absenden des Formulars neu zu berechnen.
export const input = () => ({
error: null as unknown,
validate() {
if (!this.response?.errors?.[this.$el.name]) return (this.error = null)
this.error = this.response.errors[this.$el.name]
},
});
globals.ts
Abschließend werden für diesen Schritt die für Alpine.js deklarierten Methoden importiert und im EventListener alpine:init
registriert, um auf die benötigten Scopes zugreifen zu können.
import Alpine from "alpinejs"
import { app } from "./alpine/app"
import { form } from "./alpine/form"
import { input } from "./alpine/input"
// Await Alpine.js initialization
document.addEventListener("alpine:init", () => {
Alpine.data("app", app)
Alpine.data("form", form)
Alpine.data("input", input)
})
Alpine.start()
Optionale Utility-Methoden deklarieren
Damit wir Namen für Eingabeelemente auch gleichzeitig als Label benutzen können, legen wir die Methode capitalize
an, welche Strings geschrieben in kebab-case (z. B.: "email-address"
) aufteilt und jedes Wort kapitalisiert.
Solltest du dich gegen die Kapitalisierung entscheiden, müssen die entsprechenden Referenzen in der Komponente
input.astro
entfernt werden
export const capitalize = (string: string) => {
return string.split("-").map(word => word[0].toUpperCase() + word.substring(1)).join(" ")
}
Seiten und Komponenten in Astro anlegen
Im nächsten Schritt legen wir die von uns für das Formular benötigten Seiten und Komponenten an. Hierbei definieren wir eine <Input />
Komponente und integrieren diese in den Formularblock.
input.astro
input.astro
fasst die Elemente <input />
und <label>
in einer Komponente zusammen und enthält zusätzlich die Darstellung der Validierungsfehler, die über den Alpine-Kontext input
abgebildet werden.
---
import { capitalize } from "@/scripts/utilities"
const { name, ...props } = Astro.props
---
<div
class="relative font-medium"
x-data="input"
>
<div
class="absolute pointer-events-none overflow-hidden top-3 inset-x-4 flex gap-1 items-center leading-4 text-xs transition-colors"
x-bind:class="error && 'text-rose-500'"
>
<label
class="mr-auto"
for={name}
title={capitalize(name)}
>
{capitalize(name)}{props?.required && '*'}
</label>
<div
class="ml-3 overflow-hidden flex justify-end gap-1"
x-cloak
x-show="error"
x-transition
>
<span
class="truncate"
x-text="error"
></span>
</div>
</div>
<input
class="w-full pt-7 pb-3 px-4 border rounded-xl leading-6 bg-transparent text-foreground invalid:border-rose-500 disabled:bg-muted transition-colors"
x-bind:class="error && 'border-rose-500'"
{name}
{...props}
x-effect="validate"
/>
</div>
index.astro
index.astro
repräsentiert unseren Formularblock und verwendet die vordefinierte Komponente <Input />
und ergänzt deren Logik um den Kontext form
, so dass Fehler aus dem Objekt response
dargestellt werden können.
Während unsere Komponente <Input />
die Darstelung der Validierungsfehler handhabt, binden wir das Attribut disabled
der einzelnen Eingabeelemente and den Zustand loading
, um Mehrfachsendungen des Formulars während der Verarbeitung zu unterbinden.
---
import Root from "@/layouts/root.astro"
import Input from "@/components/input.astro"
const meta = {
title: "Advanced forms with Alpine.js"
}
---
<Root {meta}>
<main>
<form
class="grid gap-2 p-6"
x-data="form"
x-on:submit.prevent="submit"
>
<Input
id="username"
name="username"
type="email"
required
placeholder="[email protected]"
x-bind:disabled="loading"
/>
<Input
id="password"
name="password"
type="password"
required
placeholder="Your password"
x-bind:disabled="loading"
/>
<button
class="bg-primary text-primary-foreground h-12 rounded-xl font-medium disabled:opacity-50 transition-opacity"
type="submit"
x-bind:disabled="loading"
>
Submit
</button>
</form>
</main>
</Root>
TL;DR
Mit Alpine.js zeigen wir, wie Validierungsfehler aus dem Backend dynamisch in einem Formular dargestellt werden und wie Eingabeelemente auf entsprechende Ereignisse im Browser reagieren.