Eine häufige Kritik an TanStack Form ist seine Standard-Ausführlichkeit. Während dies für Bildungszwecke nützlich sein kann - und das Verständnis unserer APIs fördert -, ist es für Produktionsanwendungsfälle nicht ideal.
Daher ermöglicht die grundlegende Verwendung von [tanstackField] die leistungsstärkste und flexibelste Nutzung von TanStack Form, aber wir stellen APIs zur Verfügung, die es umschließen und Ihren Anwendungscode weniger wortreich machen.
Wenn Sie TanStack Form jemals in Angular verwendet haben, um mehr als ein Eingabefeld zu binden, werden Sie schnell erkannt haben, wie viel Aufwand in jedes Eingabefeld fließt
import { Component } from '@angular/core'
import { TanStackField, injectForm, injectStore } from '@tanstack/angular-form'
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<div>
<ng-container
[tanstackField]="form"
name="firstName"
#firstName="field"
>
<label [for]="firstName.api.name">First Name:</label>
<input
[id]="firstName.api.name"
[name]="firstName.api.name"
[value]="firstName.api.state.value"
(blur)="firstName.api.handleBlur()"
(input)="firstName.api.handleChange($any($event).target.value)"
/>
@if (firstName.api.state.meta.isTouched) {
@for (error of firstName.api.state.meta.errors; track $index) {
<div style="color: red">
{{ error }}
</div>
}
}
@if (firstName.api.state.meta.isValidating) {
<p>Validating...</p>
}
</ng-container>
</div>
<div>
<ng-container
[tanstackField]="form"
name="lastName"
#lastName="field"
>
<label [for]="lastName.api.name">Last Name:</label>
<input
[id]="lastName.api.name"
[name]="lastName.api.name"
[value]="lastName.api.state.value"
(blur)="lastName.api.handleBlur()"
(input)="lastName.api.handleChange($any($event).target.value)"
/>
@if (lastName.api.state.meta.isTouched) {
@for (error of lastName.api.state.meta.errors; track $index) {
<div style="color: red">
{{ error }}
</div>
}
}
@if (lastName.api.state.meta.isValidating) {
<p>Validating...</p>
}
</ng-container>
</div>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit({ value }) {
// Do something with form data
console.log(value)
},
})
}
import { Component } from '@angular/core'
import { TanStackField, injectForm, injectStore } from '@tanstack/angular-form'
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<div>
<ng-container
[tanstackField]="form"
name="firstName"
#firstName="field"
>
<label [for]="firstName.api.name">First Name:</label>
<input
[id]="firstName.api.name"
[name]="firstName.api.name"
[value]="firstName.api.state.value"
(blur)="firstName.api.handleBlur()"
(input)="firstName.api.handleChange($any($event).target.value)"
/>
@if (firstName.api.state.meta.isTouched) {
@for (error of firstName.api.state.meta.errors; track $index) {
<div style="color: red">
{{ error }}
</div>
}
}
@if (firstName.api.state.meta.isValidating) {
<p>Validating...</p>
}
</ng-container>
</div>
<div>
<ng-container
[tanstackField]="form"
name="lastName"
#lastName="field"
>
<label [for]="lastName.api.name">Last Name:</label>
<input
[id]="lastName.api.name"
[name]="lastName.api.name"
[value]="lastName.api.state.value"
(blur)="lastName.api.handleBlur()"
(input)="lastName.api.handleChange($any($event).target.value)"
/>
@if (lastName.api.state.meta.isTouched) {
@for (error of lastName.api.state.meta.errors; track $index) {
<div style="color: red">
{{ error }}
</div>
}
}
@if (lastName.api.state.meta.isValidating) {
<p>Validating...</p>
}
</ng-container>
</div>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit({ value }) {
// Do something with form data
console.log(value)
},
})
}
Dies ist funktional korrekt, führt aber zu sehr viel wiederholtem Vorlagenverhalten immer und immer wieder. Verschieben wir stattdessen die Fehlerbehandlung, die Bindung von Labels an Eingabefelder und andere wiederholte Logik in eine Komponente
import {injectField} from '@tanstack/angular-form'
@Component({
selector: 'app-text-field',
standalone: true,
template: `
<label [for]="field.api.name">{{ label() }}</label>
<input
[id]="field.api.name"
[name]="field.api.name"
[value]="field.api.state.value"
(blur)="field.api.handleBlur()"
(input)="field.api.handleChange($any($event).target.value)"
/>
@if (field.api.state.meta.isTouched) {
@for (error of field.api.state.meta.errors; track $index) {
<div style="color: red">
{{ error }}
</div>
}
}
@if (field.api.state.meta.isValidating) {
<p>Validating...</p>
}
`,
})
export class AppTextField {
label = input.required<string>()
// This API requires another part to it from the parent component
field = injectField<string>()
}
import {injectField} from '@tanstack/angular-form'
@Component({
selector: 'app-text-field',
standalone: true,
template: `
<label [for]="field.api.name">{{ label() }}</label>
<input
[id]="field.api.name"
[name]="field.api.name"
[value]="field.api.state.value"
(blur)="field.api.handleBlur()"
(input)="field.api.handleChange($any($event).target.value)"
/>
@if (field.api.state.meta.isTouched) {
@for (error of field.api.state.meta.errors; track $index) {
<div style="color: red">
{{ error }}
</div>
}
}
@if (field.api.state.meta.isValidating) {
<p>Validating...</p>
}
`,
})
export class AppTextField {
label = input.required<string>()
// This API requires another part to it from the parent component
field = injectField<string>()
}
injectField akzeptiert ein einzelnes generisches Objekt, um den Typ von field.state.value zu definieren.
Daher würde ein numerisches Textfeld beispielsweise als injectField<number> dargestellt werden.
Jetzt können wir die TanStackAppField Direktive (tanstack-app-field) verwenden, um das erwartete Feld, das mit diesem Eingabefeld verbunden ist, bereitzustellen
import { Component } from '@angular/core'
import {
TanStackAppField,
TanStackField,
injectForm,
} from '@tanstack/angular-form'
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField, TanStackAppField, AppTextField],
template: `
<div>
<app-text-field
label="First name:"
tanstack-app-field
[tanstackField]="form"
name="firstName"
/>
</div>
<div>
<app-text-field
label="Last name:"
tanstack-app-field
[tanstackField]="form"
name="lastName"
/>
</div>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit({ value }) {
// Do something with form data
console.log(value)
},
})
}
import { Component } from '@angular/core'
import {
TanStackAppField,
TanStackField,
injectForm,
} from '@tanstack/angular-form'
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField, TanStackAppField, AppTextField],
template: `
<div>
<app-text-field
label="First name:"
tanstack-app-field
[tanstackField]="form"
name="firstName"
/>
</div>
<div>
<app-text-field
label="Last name:"
tanstack-app-field
[tanstackField]="form"
name="lastName"
/>
</div>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit({ value }) {
// Do something with form data
console.log(value)
},
})
}
Hier nimmt die tanstack-app-field Direktive die Eigenschaften von [tanstackField] und stellt sie der app-text-field Komponente zur Verfügung, damit diese leichter als Komponente verwendet werden kann.
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.