Grundlegende Konzepte und Terminologie

Diese Seite stellt die grundlegenden Konzepte und Terminologie vor, die in der @tanstack/svelte-form Bibliothek verwendet werden. Wenn Sie sich mit diesen Konzepten vertraut machen, können Sie die Bibliothek besser verstehen und mit ihr arbeiten.

Formularoptionen

Sie können Optionen für Ihr Formular erstellen, damit es von mehreren Formularen gemeinsam genutzt werden kann, indem Sie die Funktion formOptions verwenden.

Beispiel

ts
const formOpts = formOptions({
  defaultValues: {
    firstName: '',
    lastName: '',
    hobbies: [],
  } as Person,
})
const formOpts = formOptions({
  defaultValues: {
    firstName: '',
    lastName: '',
    hobbies: [],
  } as Person,
})

Formularinstanz

Eine Formularinstanz (Form Instance) ist ein Objekt, das ein einzelnes Formular darstellt und Methoden und Eigenschaften für die Arbeit mit dem Formular bereitstellt. Sie erstellen eine Formularinstanz mithilfe der Funktion createForm. Die Funktion akzeptiert ein Objekt mit einer onSubmit-Funktion, die aufgerufen wird, wenn das Formular übermittelt wird.

ts
const form = createForm(() => ({
  ...formOpts,
  onSubmit: async ({ value }) => {
    // Do something with form data
    console.log(value)
  },
}))
const form = createForm(() => ({
  ...formOpts,
  onSubmit: async ({ value }) => {
    // Do something with form data
    console.log(value)
  },
}))

Sie können auch eine Formularinstanz erstellen, ohne formOptions zu verwenden.

ts
const form = createForm<Person>(() => ({
  onSubmit: async ({ value }) => {
    // Do something with form data
    console.log(value)
  },
  defaultValues: {
    firstName: '',
    lastName: '',
    hobbies: [],
  },
}))
const form = createForm<Person>(() => ({
  onSubmit: async ({ value }) => {
    // Do something with form data
    console.log(value)
  },
  defaultValues: {
    firstName: '',
    lastName: '',
    hobbies: [],
  },
}))

Field

Ein Feld (Field) repräsentiert ein einzelnes Formulareingabeelement, z. B. ein Texteingabefeld oder eine Checkbox. Felder werden mithilfe der Komponente form.Field erstellt, die von der Formularinstanz bereitgestellt wird. Die Komponente akzeptiert eine `name`-Prop, die einem Schlüssel in den Standardwerten des Formulars entsprechen sollte. Sie akzeptiert auch eine `children`-Prop, die eine Render-Prop-Funktion ist und ein Feldobjekt als Argument entgegennimmt.

Beispiel

svelte
<form.Field name="firstName">
  {#snippet children(field)}
    <input
      name={field.name}
      value={field.state.value}
      onblur={field.handleBlur}
      oninput={(e) => field.handleChange(e.target.value)}
    />
  {/snippet}
</form.Field>
<form.Field name="firstName">
  {#snippet children(field)}
    <input
      name={field.name}
      value={field.state.value}
      onblur={field.handleBlur}
      oninput={(e) => field.handleChange(e.target.value)}
    />
  {/snippet}
</form.Field>

Feldstatus

Jedes Feld hat seinen eigenen Zustand, der seinen aktuellen Wert, den Validierungsstatus, Fehlermeldungen und andere Metadaten enthält. Sie können auf den Zustand eines Feldes über die Eigenschaft field.state zugreifen.

Beispiel

ts
const {
  value,
  meta: { errors, isValidating },
} = field.state
const {
  value,
  meta: { errors, isValidating },
} = field.state

Es gibt vier Zustände in den Metadaten, die nützlich sein können, um zu sehen, wie der Benutzer mit einem Feld interagiert.

  • "isTouched", nachdem der Benutzer das Feld geändert oder den Fokus darauf verloren hat.
  • "isDirty", nachdem sich der Wert des Feldes geändert hat, auch wenn er auf den Standardwert zurückgesetzt wurde. Das Gegenteil von isPristine.
  • "isPristine", bis der Benutzer den Feldwert ändert. Das Gegenteil von isDirty.
  • "isBlurred", nachdem der Fokus vom Feld genommen wurde.
ts
const { isTouched, isDirty, isPristine, isBlurred } = field.state.meta
const { isTouched, isDirty, isPristine, isBlurred } = field.state.meta

Field states

Verständnis von 'isDirty' in verschiedenen Bibliotheken

Nicht-persistenter dirty Zustand

  • Bibliotheken: React Hook Form (RHF), Formik, Final Form.
  • Verhalten: Ein Feld ist 'dirty' (geändert), wenn sein Wert vom Standard abweicht. Das Zurücksetzen auf den Standardwert macht es wieder 'clean' (unverändert).

Persistenter dirty Zustand

  • Bibliotheken: Angular Form, Vue FormKit.
  • Verhalten: Ein Feld bleibt 'dirty' (geändert), sobald es geändert wurde, auch wenn es auf den Standardwert zurückgesetzt wird.

Wir haben uns für das persistente 'dirty'-Zustandsmodell entschieden. Um auch einen nicht-persistenten 'dirty'-Zustand zu unterstützen, führen wir ein zusätzliches Flag ein.

  • "isDefaultValue", ob der aktuelle Wert des Feldes der Standardwert ist.
ts
const { isDefaultValue, isTouched } = field.state.meta

// The following line will re-create the non-Persistent `dirty` functionality.
const nonPersistentIsDirty = !isDefaultValue
const { isDefaultValue, isTouched } = field.state.meta

// The following line will re-create the non-Persistent `dirty` functionality.
const nonPersistentIsDirty = !isDefaultValue

Field states extended

Feld-API

Die Feld-API ist ein Objekt, das an die Render-Prop-Funktion übergeben wird, wenn ein Feld erstellt wird. Es stellt Methoden zur Bearbeitung des Feldzustands bereit.

Beispiel

svelte
<input
  name={field.name}
  value={field.state.value}
  onblur={field.handleBlur}
  oninput={(e) => field.handleChange(e.target.value)}
/>
<input
  name={field.name}
  value={field.state.value}
  onblur={field.handleBlur}
  oninput={(e) => field.handleChange(e.target.value)}
/>

Validierung

@tanstack/svelte-form bietet sowohl synchrone als auch asynchrone Validierung direkt. Validierungsfunktionen können der Komponente form.Field über die validators-Prop übergeben werden.

Beispiel

svelte
<form.Field
  name="firstName"
  validators={{
    onChange: ({ value }) =>
      !value
        ? 'A first name is required'
        : value.length < 3
          ? 'First name must be at least 3 characters'
          : undefined,
    onChangeAsync: async ({ value }) => {
      await new Promise((resolve) => setTimeout(resolve, 1000))
      return value.includes('error') && 'No "error" allowed in first name'
    },
  }}
>
  {#snippet children(field)}
    <input
      name={field.name}
      value={field.state.value}
      onBlur={field.handleBlur}
      onInput={(e) => field.handleChange(e.target.value)}
    />
    <p>{field.state.meta.errors[0]}</p>
  {/snippet}
</form.Field>
<form.Field
  name="firstName"
  validators={{
    onChange: ({ value }) =>
      !value
        ? 'A first name is required'
        : value.length < 3
          ? 'First name must be at least 3 characters'
          : undefined,
    onChangeAsync: async ({ value }) => {
      await new Promise((resolve) => setTimeout(resolve, 1000))
      return value.includes('error') && 'No "error" allowed in first name'
    },
  }}
>
  {#snippet children(field)}
    <input
      name={field.name}
      value={field.state.value}
      onBlur={field.handleBlur}
      onInput={(e) => field.handleChange(e.target.value)}
    />
    <p>{field.state.meta.errors[0]}</p>
  {/snippet}
</form.Field>

Validierung mit Standard-Schema-Bibliotheken

Zusätzlich zu handgeschriebenen Validierungsoptionen unterstützen wir auch die Standard Schema Spezifikation.

Sie können ein Schema mit einer beliebigen der Bibliotheken definieren, die die Spezifikation implementieren, und es an einen Formular- oder Feldvalidator übergeben.

Unterstützte Bibliotheken umfassen:

  • Zod (v3.24.0 oder höher)
  • Valibot (v1.0.0 oder höher)
  • ArkType (v2.1.20 oder höher)
  • Yup (v1.7.0 oder höher)
svelte
<script>
  import { z } from 'zod'

  // ...
</script>

<form.Field
  name="firstName"
  validators={{
    onChange: z.string().min(3, 'First name must be at least 3 characters'),
    onChangeAsyncDebounceMs: 500,
    onChangeAsync: z.string().refine(
      async (value) => {
        await new Promise((resolve) => setTimeout(resolve, 1000))
        return !value.includes('error')
      },
      {
        message: 'No "error" allowed in first name',
      },
    ),
  }}
>
  {#snippet children(field)}
    <input
      name={field.name}
      value={field.state.value}
      onBlur={field.handleBlur}
      onInput={(e) => field.handleChange(e.target.value)}
    />
    <p>{field.state.meta.errors[0]}</p>
  {/snippet}
</form.Field>
<script>
  import { z } from 'zod'

  // ...
</script>

<form.Field
  name="firstName"
  validators={{
    onChange: z.string().min(3, 'First name must be at least 3 characters'),
    onChangeAsyncDebounceMs: 500,
    onChangeAsync: z.string().refine(
      async (value) => {
        await new Promise((resolve) => setTimeout(resolve, 1000))
        return !value.includes('error')
      },
      {
        message: 'No "error" allowed in first name',
      },
    ),
  }}
>
  {#snippet children(field)}
    <input
      name={field.name}
      value={field.state.value}
      onBlur={field.handleBlur}
      onInput={(e) => field.handleChange(e.target.value)}
    />
    <p>{field.state.meta.errors[0]}</p>
  {/snippet}
</form.Field>

Reaktivität

@tanstack/svelte-form bietet verschiedene Möglichkeiten, sich über Änderungen des Formulars und der Feldzustände zu informieren, insbesondere den Hook form.useStore und die Komponente form.Subscribe. Diese Methoden ermöglichen es Ihnen, die Rendering-Leistung Ihres Formulars zu optimieren, indem Komponenten nur dann aktualisiert werden, wenn es notwendig ist.

Beispiel

svelte
<script>
  //...
  const firstName = form.useStore((state) => state.values.firstName)
</script>

<form.Subscribe
  selector={(state) => ({
    canSubmit: state.canSubmit,
    isSubmitting: state.isSubmitting,
  })}
>
  {#snippet children(state)}
    <button type="submit" disabled={!state.canSubmit}>
      {state.isSubmitting ? '...' : 'Submit'}
    </button>
  {/snippet}
</form.Subscribe>
<script>
  //...
  const firstName = form.useStore((state) => state.values.firstName)
</script>

<form.Subscribe
  selector={(state) => ({
    canSubmit: state.canSubmit,
    isSubmitting: state.isSubmitting,
  })}
>
  {#snippet children(state)}
    <button type="submit" disabled={!state.canSubmit}>
      {state.isSubmitting ? '...' : 'Submit'}
    </button>
  {/snippet}
</form.Subscribe>

Array-Felder

Array-Felder ermöglichen es Ihnen, eine Liste von Werten innerhalb eines Formulars zu verwalten, wie z. B. eine Liste von Hobbys. Sie können ein Array-Feld mit der form.Field Komponente mit der Prop mode="array" erstellen.

Wenn Sie mit Array-Feldern arbeiten, können Sie die Methoden pushValue, removeValue, swapValues und moveValue des Feldes verwenden, um Werte im Array hinzuzufügen, zu entfernen und zu tauschen.

Beispiel

svelte
<form.Field name="hobbies" mode="array">
  {#snippet children(hobbiesField)}
    <div>
      Hobbies
      <div>
        {#each hobbiesField.state.value as _, i}
            <div>
              <form.Field name={`hobbies[${i}].name`}>
                {#snippet children(field)}
                  <div>
                    <label for={field.name}>Name:</label>
                    <input
                      id={field.name}
                      name={field.name}
                      value={field.state.value}
                      onblur={field.handleBlur}
                      onchange={(e) => field.handleChange(e.target.value)}
                    />
                    <button
                      type="button"
                      onclick={() => hobbiesField.removeValue(i)}
                    >
                      X
                    </button>
                  </div>
                {/snippet}
              </form.Field>
              <form.Field name={`hobbies[${i}].description`}>
                {#snippet children(field)}
                    <div>
                      <label for={field.name}>Description:</label>
                      <input
                        id={field.name}
                        name={field.name}
                        value={field.state.value}
                        onblur={field.handleBlur}
                        onchange={(e) => field.handleChange(e.target.value)}
                      />
                    </div>
                {/snippet}
              </form.Field>
            </div>
          {:else}
            No hobbies found.
          {/each}
      </div>
      <button
        type="button"
        onclick={() =>
          hobbiesField.pushValue({
            name: '',
            description: '',
            yearsOfExperience: 0,
          })
        }
      >
        Add hobby
      </button>
    </div>
  {/snippet}
</form.Field>
<form.Field name="hobbies" mode="array">
  {#snippet children(hobbiesField)}
    <div>
      Hobbies
      <div>
        {#each hobbiesField.state.value as _, i}
            <div>
              <form.Field name={`hobbies[${i}].name`}>
                {#snippet children(field)}
                  <div>
                    <label for={field.name}>Name:</label>
                    <input
                      id={field.name}
                      name={field.name}
                      value={field.state.value}
                      onblur={field.handleBlur}
                      onchange={(e) => field.handleChange(e.target.value)}
                    />
                    <button
                      type="button"
                      onclick={() => hobbiesField.removeValue(i)}
                    >
                      X
                    </button>
                  </div>
                {/snippet}
              </form.Field>
              <form.Field name={`hobbies[${i}].description`}>
                {#snippet children(field)}
                    <div>
                      <label for={field.name}>Description:</label>
                      <input
                        id={field.name}
                        name={field.name}
                        value={field.state.value}
                        onblur={field.handleBlur}
                        onchange={(e) => field.handleChange(e.target.value)}
                      />
                    </div>
                {/snippet}
              </form.Field>
            </div>
          {:else}
            No hobbies found.
          {/each}
      </div>
      <button
        type="button"
        onclick={() =>
          hobbiesField.pushValue({
            name: '',
            description: '',
            yearsOfExperience: 0,
          })
        }
      >
        Add hobby
      </button>
    </div>
  {/snippet}
</form.Field>

Dies sind die grundlegenden Konzepte und Terminologie, die in der @tanstack/svelte-form Bibliothek verwendet werden. Das Verständnis dieser Konzepte wird Ihnen helfen, effektiver mit der Bibliothek zu arbeiten und komplexe Formulare mühelos zu erstellen.

Unsere Partner
Code Rabbit
Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.

Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.