The TanStackField directive provides a way to create and manage form fields in Angular applications using TanStack Form.
Import
import { TanStackField } from '@tanstack/angular-form'
Selector
tanstackField
FormApi<TParentData>
required
The parent form API instance.
The field name as a path string (e.g., ‘user.firstName’ or ‘items[0].name’).
The default value for the field.
validators
FieldValidators<TParentData, TName, TData>
Validation functions for the field.validators.onChange
FieldValidateOrFn<TParentData, TName, TData>
Validator that runs on every change.
validators.onChangeAsync
FieldAsyncValidateOrFn<TParentData, TName, TData>
Async validator that runs on change.
validators.onChangeAsyncDebounceMs
Debounce time in milliseconds for async validation.
validators.onBlur
FieldValidateOrFn<TParentData, TName, TData>
Validator that runs when the field loses focus.
validators.onMount
FieldValidateOrFn<TParentData, TName, TData>
Validator that runs when the field is mounted.
Debounce time in milliseconds for async validation. Default is 500ms.
If true, async validation runs even if sync validation fails.
The field mode. Use ‘array’ for array fields to enable array-specific methods.
If true, errors won’t be flattened to a single array.
Export As
The directive can be exported with a template reference variable to access the field API.
Exported Properties
api
FieldApi<TParentData, TName, TData>
The field API instance.The current field state.Field metadata.Current validation errors.
Whether the field has been touched.
Whether the field value has changed.
api.state.meta.isValidating
Whether async validation is in progress.
Mark the field as touched.
api.pushValue
(value: TData extends Array<infer U> ? U : never) => void
Add a value to an array field (only available when mode=‘array’).
Remove a value from an array field (only available when mode=‘array’).
Usage Example
Basic Field
import { Component } from '@angular/core'
import { injectForm, TanStackField } from '@tanstack/angular-form'
import type { FieldValidateFn } from '@tanstack/angular-form'
@Component({
selector: 'app-form',
standalone: true,
imports: [TanStackField],
template: `
<form (submit)="handleSubmit($event)">
<ng-container
[tanstackField]="form"
name="email"
[validators]="{ onChange: emailValidator }"
#email="field"
>
<label [for]="email.api.name">Email:</label>
<input
[id]="email.api.name"
[name]="email.api.name"
[value]="email.api.state.value"
(blur)="email.api.handleBlur()"
(input)="email.api.handleChange($any($event).target.value)"
/>
@if (email.api.state.meta.isTouched && email.api.state.meta.errors.length > 0) {
<div style="color: red">
{{ email.api.state.meta.errors[0] }}
</div>
}
</ng-container>
<button type="submit">Submit</button>
</form>
`,
})
export class FormComponent {
emailValidator: FieldValidateFn<any, string, any> = ({ value }) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return !emailRegex.test(value) ? 'Invalid email address' : undefined
}
form = injectForm({
defaultValues: {
email: '',
},
onSubmit({ value }) {
console.log('Email:', value.email)
},
})
handleSubmit(event: SubmitEvent) {
event.preventDefault()
this.form.handleSubmit()
}
}
Array Field
import { Component } from '@angular/core'
import { injectForm, TanStackField } from '@tanstack/angular-form'
@Component({
selector: 'app-array-form',
standalone: true,
imports: [TanStackField],
template: `
<form>
<ng-container
[tanstackField]="form"
name="items"
mode="array"
#items="field"
>
@for (item of items.api.state.value; track $index) {
<div>
<ng-container
[tanstackField]="form"
[name]="'items[' + $index + '].name'"
#itemField="field"
>
<input
[value]="itemField.api.state.value"
(input)="itemField.api.handleChange($any($event).target.value)"
/>
</ng-container>
<button (click)="items.api.removeValue($index)" type="button">
Remove
</button>
</div>
}
<button (click)="items.api.pushValue({ name: '' })" type="button">
Add Item
</button>
</ng-container>
</form>
`,
})
export class ArrayFormComponent {
form = injectForm({
defaultValues: {
items: [] as Array<{ name: string }>,
},
onSubmit({ value }) {
console.log(value)
},
})
}
Field with Async Validation
import { Component } from '@angular/core'
import { injectForm, TanStackField } from '@tanstack/angular-form'
import type { FieldValidateAsyncFn } from '@tanstack/angular-form'
@Component({
selector: 'app-async-form',
standalone: true,
imports: [TanStackField],
template: `
<ng-container
[tanstackField]="form"
name="username"
[validators]="{
onChange: usernameValidator,
onChangeAsync: usernameAsyncValidator,
onChangeAsyncDebounceMs: 500,
}"
#username="field"
>
<input
[value]="username.api.state.value"
(input)="username.api.handleChange($any($event).target.value)"
/>
@if (username.api.state.meta.isValidating) {
<p>Validating...</p>
}
@if (username.api.state.meta.errors.length > 0) {
<div style="color: red">
{{ username.api.state.meta.errors[0] }}
</div>
}
</ng-container>
`,
})
export class AsyncFormComponent {
usernameValidator = ({ value }: { value: string }) =>
value.length < 3 ? 'Username must be at least 3 characters' : undefined
usernameAsyncValidator: FieldValidateAsyncFn<any, string, any> = async ({
value,
}) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return value === 'taken' ? 'Username already taken' : undefined
}
form = injectForm({
defaultValues: {
username: '',
},
onSubmit({ value }) {
console.log(value)
},
})
}
See Also