Input-Bindings — bind:value & bind:checked
Formulareingaben direkt an reaktiven State koppeln — ohne einen einzigen Event-Handler von Hand zu schreiben.
Das Problem: zwei Richtungen
Ein Eingabefeld hat immer zwei Richtungen: Der State muss den angezeigten
Wert bestimmen, und eine Eingabe des Nutzers muss den State aktualisieren. Ohne Hilfsmittel
verdrahtest du beide von Hand — value hinein, oninput zurück:
<script>
let name = $state('');
</script>
<!-- Ohne bind: musst du beide Richtungen selbst verdrahten -->
<input value={name} oninput={(e) => (name = e.currentTarget.value)} />
<p>Hallo, {name}!</p>Das funktioniert, ist aber umständlich und fehleranfällig. Svelte bietet dafür bind:.
bind:value — Zwei-Wege-Bindung
bind:value verbindet ein Eingabefeld in beide Richtungen mit einer $state-Variable. Ändert sich der State, aktualisiert sich das Feld; tippt der
Nutzer, aktualisiert sich der State.
<script>
let name = $state('');
</script>
<!-- Mit bind:value erledigt Svelte beide Richtungen -->
<input bind:value={name} />
<p>Hallo, {name}!</p>ℹ️ bind: ist nur Zucker
bind:value={name} ist exakt die Kurzform des vorigen Beispiels — Svelte
setzt intern value={name} und einen oninput-Handler,
der name zurückschreibt. Es passiert nichts Magisches: Es bleibt reaktiver
State, den du auch anderswo lesen und setzen kannst.
Typ-Coercion bei number & range
Ein HTML-Eingabefeld liefert seinen Wert grundsätzlich als String. Bei type="number" und type="range" nimmt Svelte dir die Umwandlung ab
und bindet automatisch eine echte Zahl — kein parseInt nötig.
<script>
let alter = $state(0);
let lautstaerke = $state(50);
</script>
<!-- type="number" und type="range" liefern automatisch Number, nicht String -->
<input type="number" bind:value={alter} />
<input type="range" min="0" max="100" bind:value={lautstaerke} />
<!-- alter ist wirklich eine Zahl: -->
<p>{alter + 1}</p> <!-- 31, nicht "301" -->⚠️ Ohne bind: bekommst du Strings
Nur bind:value übernimmt die Coercion. Liest du den Wert über einen eigenen oninput-Handler aus (e.currentTarget.value), erhältst du einen
String — dann würde "30" + 1 zu "301" statt 31.
bind:checked — Checkboxen
Eine Checkbox hat keinen sinnvollen value, sondern einen Zustand
„an/aus". Dafür gibt es bind:checked, das an einen boolean bindet:
<script>
let einverstanden = $state(false);
</script>
<!-- bind:checked statt bind:value für Checkboxen -->
<label>
<input type="checkbox" bind:checked={einverstanden} />
AGB akzeptieren
</label>
{#if einverstanden}
<button>Weiter</button>
{/if}Live: Formular an ein State-Objekt binden
In der Praxis bündelst du mehrere Felder in einem $state-Objekt. Jedes Feld
bindet an eine Eigenschaft — und das Objekt ist jederzeit ein exaktes Abbild des Formulars:
<script>
let profil = $state({
name: '',
alter: 0,
bio: '',
newsletter: false
});
</script>
<input bind:value={profil.name} />
<input type="number" bind:value={profil.alter} />
<textarea bind:value={profil.bio}></textarea>
<input type="checkbox" bind:checked={profil.newsletter} />
<!-- profil ist jederzeit ein aktuelles Abbild des Formulars -->
<pre>{JSON.stringify(profil, null, 2)}</pre>Live-Vorschau des $state-Objekts:
{
"name": "Anna",
"alter": 30,
"bio": "Rails-Entwicklerin, neu bei Svelte.",
"newsletter": true
} Tippe oben — die Vorschau folgt bei jedem Tastendruck. Beachte: alter steht ohne Anführungszeichen da, ist also eine echte Zahl.
Rails-Vergleich: sofort statt nach dem Roundtrip
🛤️ form_with & params vs. bind:
In Rails leben Formularwerte auf dem Server: Der Nutzer füllt aus, drückt Submit, und erst der nächste Request liefert die Daten als params. Eine Live-Vorschau während des Tippens erfordert zusätzliches
JavaScript (Stimulus, Turbo Frames o. Ä.).
Mit bind: ist der Zustand sofort im Browser verfügbar — ganz
ohne Roundtrip. Das Absenden an einen Server ist ein separater, bewusster Schritt (Modul M9, Form Actions), nicht die Voraussetzung dafür, den Wert zu kennen.
# Rails: Werte reisen erst beim Submit als params zum Server
<%= form_with model: @profil do |f| %>
<%= f.text_field :name %>
<%= f.number_field :alter %>
<%= f.text_area :bio %>
<%= f.check_box :newsletter %>
<%= f.submit %>
<% end %>
# Erst nach dem Roundtrip liegt params[:profil][:name] vor.
# Eine Live-Vorschau bräuchte zusätzlich Stimulus/Turbo + JS.Zusammenfassung
💡 Das Wichtigste
bind:valuekoppelt Text-, Number-, Range- und Textarea-Felder in beide Richtungen.bind:checkedbindet Checkboxen an einenboolean.bind:ist nur Zucker übervalue+oninput— kein eigener Handler nötig.- Bei
type="number"/rangebekommst du automatisch echte Zahlen. - Bündle mehrere Felder in einem
$state-Objekt für sauberen Code.
🎯 Probier es selbst
Ergänze das Profil-Formular um ein Feld lautstaerke als <input type="range" min="0" max="100"> mit bind:value. Zeige
den Wert daneben an und beobachte in der Vorschau, dass er als Zahl (nicht als String)
erscheint.