TanStack Form 常被批评之处在于其开箱即用的冗长。虽然这可能有助于教学——帮助巩固对 API 的理解——但在生产用例中并非理想选择。
因此,虽然 [tanstackField] 的基本用法实现了 TanStack Form 最强大、最灵活的用法,但我们仍然提供了包装它的 API,使您的应用程序代码更加简洁。
如果您曾经在 Angular 中使用 TanStack Form 绑定多个输入,您很快就会意识到每个输入需要多少工作量。
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)
},
})
}
这在功能上是正确的,但引入了大量重复的模板化行为。相反,让我们将错误处理、标签到输入的绑定以及其他重复的逻辑移到一个组件中。
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 接受一个泛型来定义 field.state.value 的类型。
因此,例如,一个数字文本字段将表示为 injectField<number>。
现在,我们可以使用 TanStackAppField 指令(tanstack-app-field)来 提供与此输入关联的预期字段。
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)
},
})
}
在这里,tanstack-app-field 指令获取 [tanstackField] 的属性,并将其 提供给 app-text-field,以便它们可以作为组件更容易地使用。
您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。