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">名字:</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>验证中...</p>
}
</ng-container>
</div>
<div>
<ng-container
[tanstackField]="form"
name="lastName"
#lastName="field"
>
<label [for]="lastName.api.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>验证中...</p>
}
</ng-container>
</div>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit({ value }) {
// 对表单数据进行处理
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">名字:</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>验证中...</p>
}
</ng-container>
</div>
<div>
<ng-container
[tanstackField]="form"
name="lastName"
#lastName="field"
>
<label [for]="lastName.api.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>验证中...</p>
}
</ng-container>
</div>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit({ value }) {
// 对表单数据进行处理
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>()
// 此 API 需要来自父组件的另一部分
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>()
// 此 API 需要来自父组件的另一部分
field = injectField<string>()
}
injectField 接受单个泛型来定义 field.state.value 类型。
因此,例如,数字文本字段将表示为 injectField<number>。
现在,我们可以使用 TanStackAppField 指令(tanstack-app-field)来 provide 与此输入关联的预期字段:
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="名字:"
tanstack-app-field
[tanstackField]="form"
name="firstName"
/>
</div>
<div>
<app-text-field
label="姓氏:"
tanstack-app-field
[tanstackField]="form"
name="lastName"
/>
</div>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit({ value }) {
// 对表单数据进行处理
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="名字:"
tanstack-app-field
[tanstackField]="form"
name="firstName"
/>
</div>
<div>
<app-text-field
label="姓氏:"
tanstack-app-field
[tanstackField]="form"
name="lastName"
/>
</div>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit({ value }) {
// 对表单数据进行处理
console.log(value)
},
})
}
在这里,tanstack-app-field 指令从 [tanstackField] 获取属性并将它们 provide 给 app-text-field,以便它们可以更容易地作为组件使用。