Diese Seite führt die grundlegenden Konzepte und Terminologie ein, die in der @tanstack/angular-form Bibliothek verwendet werden. Die Vertrautheit mit diesen Konzepten wird Ihnen helfen, die Bibliothek besser zu verstehen und mit ihr zu arbeiten.
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 mit der Funktion injectForm. Der Hook akzeptiert ein Objekt mit einer onSubmit Funktion, die aufgerufen wird, wenn das Formular übermittelt wird.
const form = injectForm({
onSubmit: async ({ value }) => {
// Do something with form data
console.log(value)
},
})
const form = injectForm({
onSubmit: async ({ value }) => {
// Do something with form data
console.log(value)
},
})
Ein Feld (Field) repräsentiert ein einzelnes Formulareingabeelement, wie z. B. eine Texteingabe oder eine Checkbox. Felder werden mit der Direktive tanstackField erstellt. Die Direktive akzeptiert eine `name`-Prop, die einem Schlüssel in den Standardwerten des Formulars entsprechen sollte. Sie stellt auch eine Instanz namens field der Interna der Direktive zur Verfügung, die über eine Vorlagenvariable verwendet werden sollte, um auf die Interna des Feldes zuzugreifen.
Beispiel
<ng-container [tanstackField]="form" name="firstName" #firstName="field">
<input
[value]="firstName.api.state.value"
(blur)="firstName.api.handleBlur()"
(input)="firstName.api.handleChange($any($event).target.value)"
/>
</ng-container>
<ng-container [tanstackField]="form" name="firstName" #firstName="field">
<input
[value]="firstName.api.state.value"
(blur)="firstName.api.handleBlur()"
(input)="firstName.api.handleChange($any($event).target.value)"
/>
</ng-container>
Jedes Feld hat seinen eigenen Zustand (state), der seinen aktuellen Wert, den Validierungsstatus, Fehlermeldungen und andere Metadaten umfasst. Sie können auf den Zustand eines Feldes über die Eigenschaft fieldApi.state zugreifen.
Beispiel
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.
const { isTouched, isDirty, isPristine, isBlurred } = field.state.meta
const { isTouched, isDirty, isPristine, isBlurred } = field.state.meta

Nicht-persistenter dirty Zustand
Persistenter dirty Zustand
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.
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

Die Feld-API (Field API) ist ein Objekt, auf das über die Eigenschaft tanstackField.api beim Erstellen eines Feldes zugegriffen wird. Sie bietet Methoden für die Arbeit mit dem Zustand des Feldes.
Beispiel
<input
[value]="fieldName.api.state.value"
(blur)="fieldName.api.handleBlur()"
(input)="fieldName.api.handleChange($any($event).target.value)"
/>
<input
[value]="fieldName.api.state.value"
(blur)="fieldName.api.handleBlur()"
(input)="fieldName.api.handleChange($any($event).target.value)"
/>
@tanstack/angular-form bietet sowohl synchrone als auch asynchrone Validierung "out of the box". Validierungsfunktionen können über die Prop validators an die Direktive tanstackField übergeben werden.
Beispiel
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container [tanstackField]="form" name="firstName" #firstName="field">
<input
[value]="firstName.api.state.value"
(blur)="firstName.api.handleBlur()"
(input)="firstName.api.handleChange($any($event).target.value)"
/>
</ng-container>
`,
})
export class AppComponent {
firstNameValidator: FieldValidateFn<any, any, string, any> = ({
value,
}) =>
!value
? 'A first name is required'
: value.length < 3
? 'First name must be at least 3 characters'
: undefined
firstNameAsyncValidator: FieldValidateAsyncFn<any, string, any> =
async ({ value }) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return value.includes('error') && 'No "error" allowed in first name'
}
form = injectForm({
defaultValues: {
firstName: '',
},
onSubmit({ value }) {
console.log(value)
},
})
}
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container [tanstackField]="form" name="firstName" #firstName="field">
<input
[value]="firstName.api.state.value"
(blur)="firstName.api.handleBlur()"
(input)="firstName.api.handleChange($any($event).target.value)"
/>
</ng-container>
`,
})
export class AppComponent {
firstNameValidator: FieldValidateFn<any, any, string, any> = ({
value,
}) =>
!value
? 'A first name is required'
: value.length < 3
? 'First name must be at least 3 characters'
: undefined
firstNameAsyncValidator: FieldValidateAsyncFn<any, string, any> =
async ({ value }) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return value.includes('error') && 'No "error" allowed in first name'
}
form = injectForm({
defaultValues: {
firstName: '',
},
onSubmit({ value }) {
console.log(value)
},
})
}
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:
Beispiel
import { z } from 'zod'
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container
[tanstackField]="form"
name="firstName"
[validators]="{
onChange: z.string().min(3, 'First name must be at least 3 characters'),
onChangeAsyncDebounceMs: 500,
onChangeAsync: firstNameAsyncValidator
}"
#firstName="field"
>
<!-- ... -->
</ng-container>
`,
})
export class AppComponent {
firstNameAsyncValidator = z.string().refine(
async (value) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return !value.includes('error')
},
{
message: "No 'error' allowed in first name",
},
)
form = injectForm({
defaultValues: {
firstName: '',
},
onSubmit({ value }) {
// Do something with form data
console.log(value)
},
})
z = z
}
import { z } from 'zod'
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container
[tanstackField]="form"
name="firstName"
[validators]="{
onChange: z.string().min(3, 'First name must be at least 3 characters'),
onChangeAsyncDebounceMs: 500,
onChangeAsync: firstNameAsyncValidator
}"
#firstName="field"
>
<!-- ... -->
</ng-container>
`,
})
export class AppComponent {
firstNameAsyncValidator = z.string().refine(
async (value) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return !value.includes('error')
},
{
message: "No 'error' allowed in first name",
},
)
form = injectForm({
defaultValues: {
firstName: '',
},
onSubmit({ value }) {
// Do something with form data
console.log(value)
},
})
z = z
}
@tanstack/angular-form bietet eine Möglichkeit, Formular- und Feldzustandsänderungen über injectStore(this.form, selector) zu abonnieren.
Beispiel
import { injectForm, injectStore } from '@tanstack/angular-form'
@Component(/*...*/)
class AppComponent {
form = injectForm(/*...*/)
canSubmit = injectStore(this.form, (state) => state.canSubmit)
isSubmitting = injectStore(this.form, (state) => state.isSubmitting)
}
import { injectForm, injectStore } from '@tanstack/angular-form'
@Component(/*...*/)
class AppComponent {
form = injectForm(/*...*/)
canSubmit = injectStore(this.form, (state) => state.canSubmit)
isSubmitting = injectStore(this.form, (state) => state.isSubmitting)
}
@tanstack/angular-form ermöglicht es Ihnen, auf bestimmte Auslöser (Triggers) zu reagieren und diese zu "beobachten", um Seiteneffekte auszulösen.
Beispiel
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container
[tanstackField]="form"
name="country"
[listeners]="{
onChange: onCountryChange
}"
#country="field"
></ng-container>
`,
})
...
onCountryChange: FieldListenerFn<any, any, any, any, string> = ({
value,
}) => {
console.log(`Country changed to: ${value}, resetting province`)
this.form.setFieldValue('province', '')
}
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container
[tanstackField]="form"
name="country"
[listeners]="{
onChange: onCountryChange
}"
#country="field"
></ng-container>
`,
})
...
onCountryChange: FieldListenerFn<any, any, any, any, string> = ({
value,
}) => {
console.log(`Country changed to: ${value}, resetting province`)
this.form.setFieldValue('province', '')
}
Weitere Informationen finden Sie unter Listener
Array-Felder ermöglichen es Ihnen, eine Liste von Werten innerhalb eines Formulars zu verwalten, z. B. eine Liste von Hobbys. Sie können ein Array-Feld mit der Direktive tanstackField erstellen.
Beim Arbeiten mit Array-Feldern können Sie die Methoden pushValue, removeValue und swapValues des Feldes verwenden, um Werte im Array hinzuzufügen, zu entfernen und zu tauschen.
Beispiel
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container [tanstackField]="form" name="hobbies" #hobbies="field">
<div>
Hobbies
<div>
@if (!hobbies.api.state.value.length) {
No hobbies found
}
@for (_ of hobbies.api.state.value; track $index) {
<div>
<ng-container
[tanstackField]="form"
[name]="getHobbyName($index)"
#hobbyName="field"
>
<div>
<label [for]="hobbyName.api.name">Name:</label>
<input
[id]="hobbyName.api.name"
[name]="hobbyName.api.name"
[value]="hobbyName.api.state.value"
(blur)="hobbyName.api.handleBlur()"
(input)="
hobbyName.api.handleChange($any($event).target.value)
"
/>
<button
type="button"
(click)="hobbies.api.removeValue($index)"
>
X
</button>
</div>
</ng-container>
<ng-container
[tanstackField]="form"
[name]="getHobbyDesc($index)"
#hobbyDesc="field"
>
<div>
<label [for]="hobbyDesc.api.name">Description:</label>
<input
[id]="hobbyDesc.api.name"
[name]="hobbyDesc.api.name"
[value]="hobbyDesc.api.state.value"
(blur)="hobbyDesc.api.handleBlur()"
(input)="
hobbyDesc.api.handleChange($any($event).target.value)
"
/>
</div>
</ng-container>
</div>
}
</div>
<button type="button" (click)="hobbies.api.pushValue(defaultHobby)">
Add hobby
</button>
</div>
</ng-container>
`,
})
export class AppComponent {
defaultHobby = {
name: '',
description: '',
yearsOfExperience: 0,
}
getHobbyName = (idx: number) => `hobbies[${idx}].name` as const;
getHobbyDesc = (idx: number) => `hobbies[${idx}].description` as const;
form = injectForm({
defaultValues: {
hobbies: [] as Array<{
name: string
description: string
yearsOfExperience: number
}>,
},
onSubmit({ value }) {
alert(JSON.stringify(value))
},
})
}
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container [tanstackField]="form" name="hobbies" #hobbies="field">
<div>
Hobbies
<div>
@if (!hobbies.api.state.value.length) {
No hobbies found
}
@for (_ of hobbies.api.state.value; track $index) {
<div>
<ng-container
[tanstackField]="form"
[name]="getHobbyName($index)"
#hobbyName="field"
>
<div>
<label [for]="hobbyName.api.name">Name:</label>
<input
[id]="hobbyName.api.name"
[name]="hobbyName.api.name"
[value]="hobbyName.api.state.value"
(blur)="hobbyName.api.handleBlur()"
(input)="
hobbyName.api.handleChange($any($event).target.value)
"
/>
<button
type="button"
(click)="hobbies.api.removeValue($index)"
>
X
</button>
</div>
</ng-container>
<ng-container
[tanstackField]="form"
[name]="getHobbyDesc($index)"
#hobbyDesc="field"
>
<div>
<label [for]="hobbyDesc.api.name">Description:</label>
<input
[id]="hobbyDesc.api.name"
[name]="hobbyDesc.api.name"
[value]="hobbyDesc.api.state.value"
(blur)="hobbyDesc.api.handleBlur()"
(input)="
hobbyDesc.api.handleChange($any($event).target.value)
"
/>
</div>
</ng-container>
</div>
}
</div>
<button type="button" (click)="hobbies.api.pushValue(defaultHobby)">
Add hobby
</button>
</div>
</ng-container>
`,
})
export class AppComponent {
defaultHobby = {
name: '',
description: '',
yearsOfExperience: 0,
}
getHobbyName = (idx: number) => `hobbies[${idx}].name` as const;
getHobbyDesc = (idx: number) => `hobbies[${idx}].description` as const;
form = injectForm({
defaultValues: {
hobbies: [] as Array<{
name: string
description: string
yearsOfExperience: number
}>,
},
onSubmit({ value }) {
alert(JSON.stringify(value))
},
})
}
Dies sind die grundlegenden Konzepte und die Terminologie, die in der @tanstack/angular-form Bibliothek verwendet werden. Das Verständnis dieser Konzepte wird Ihnen helfen, effektiver mit der Bibliothek zu arbeiten und komplexe Formulare einfach zu erstellen.
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.