动态验证

在很多情况下,您希望根据表单的状态或其他条件更改验证规则。最常见的例子是,您希望根据用户是第一次提交表单还是不是,以不同的方式验证一个字段。

我们通过我们的 onDynamic 验证函数来支持这一点。

angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <!-- Your form template here -->
  `,
})
export class AppComponent {
  form = injectForm({
    defaultValues: {
      firstName: '',
      lastName: '',
    },
    // If this is omitted, onDynamic will not be called
    validationLogic: revalidateLogic(),
    validators: {
      onDynamic: ({ value }) => {
        if (!value.firstName) {
          return { firstName: 'A first name is required' }
        }
        return undefined
      },
    },
  })
}
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <!-- Your form template here -->
  `,
})
export class AppComponent {
  form = injectForm({
    defaultValues: {
      firstName: '',
      lastName: '',
    },
    // If this is omitted, onDynamic will not be called
    validationLogic: revalidateLogic(),
    validators: {
      onDynamic: ({ value }) => {
        if (!value.firstName) {
          return { firstName: 'A first name is required' }
        }
        return undefined
      },
    },
  })
}

默认情况下,不会调用 onDynamic,因此您需要将 revalidateLogic() 传递给 injectFormvalidationLogic 选项。

重新验证选项

revalidateLogic 允许您指定何时运行验证,并根据表单的当前提交状态动态更改验证规则。

它接受两个参数

  • mode: 首次提交表单之前的验证模式。可以是以下任一选项:

    • change: 每次更改时验证。
    • blur: 失去焦点时验证。
    • submit: 提交时验证。(默认
  • modeAfterSubmission: 表单提交后的验证模式。可以是以下任一选项:

    • change: 每次更改时验证。(默认
    • blur: 失去焦点时验证。
    • submit: 提交时验证。

例如,您可以使用以下方式在首次提交后根据失去焦点来重新验证:

angular-ts
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <!-- Your form template here -->
  `,
})
export class AppComponent {
  form = injectForm({
    // ...
    validationLogic: revalidateLogic({
      mode: 'submit',
      modeAfterSubmission: 'blur',
    }),
    // ...
  })
}
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <!-- Your form template here -->
  `,
})
export class AppComponent {
  form = injectForm({
    // ...
    validationLogic: revalidateLogic({
      mode: 'submit',
      modeAfterSubmission: 'blur',
    }),
    // ...
  })
}

访问错误

就像您可以从 onChangeonBlur 验证中访问错误一样,您可以通过 injectStore 访问表单错误映射中 onDynamic 验证函数的错误。

angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, injectStore, revalidateLogic } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <p>{{ formErrorMap().onDynamic?.firstName }}</p>
  `,
})
export class AppComponent {
  form = injectForm({
    // ...
    validationLogic: revalidateLogic(),
    validators: {
      onDynamic: ({ value }) => {
        if (!value.firstName) {
          return { firstName: 'A first name is required' }
        }
        return undefined
      },
    },
  })

  formErrorMap = injectStore(this.form, (state) => state.errorMap)
}
import { Component } from '@angular/core'
import { TanStackField, injectForm, injectStore, revalidateLogic } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <p>{{ formErrorMap().onDynamic?.firstName }}</p>
  `,
})
export class AppComponent {
  form = injectForm({
    // ...
    validationLogic: revalidateLogic(),
    validators: {
      onDynamic: ({ value }) => {
        if (!value.firstName) {
          return { firstName: 'A first name is required' }
        }
        return undefined
      },
    },
  })

  formErrorMap = injectStore(this.form, (state) => state.errorMap)
}

与其他验证逻辑结合使用

您可以将 onDynamic 验证与其他验证逻辑一起使用,例如 onChangeonBlur

angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, injectStore, revalidateLogic } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <div>
      <p>{{ formErrorMap().onChange?.firstName }}</p>
      <p>{{ formErrorMap().onDynamic?.lastName }}</p>
    </div>
  `,
})
export class AppComponent {
  form = injectForm({
    defaultValues: {
      firstName: '',
      lastName: '',
    },
    validationLogic: revalidateLogic(),
    validators: {
      onChange: ({ value }) => {
        if (!value.firstName) {
          return { firstName: 'A first name is required' }
        }
        return undefined
      },
      onDynamic: ({ value }) => {
        if (!value.lastName) {
          return { lastName: 'A last name is required' }
        }
        return undefined
      },
    },
  })

  formErrorMap = injectStore(this.form, (state) => state.errorMap)
}
import { Component } from '@angular/core'
import { TanStackField, injectForm, injectStore, revalidateLogic } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <div>
      <p>{{ formErrorMap().onChange?.firstName }}</p>
      <p>{{ formErrorMap().onDynamic?.lastName }}</p>
    </div>
  `,
})
export class AppComponent {
  form = injectForm({
    defaultValues: {
      firstName: '',
      lastName: '',
    },
    validationLogic: revalidateLogic(),
    validators: {
      onChange: ({ value }) => {
        if (!value.firstName) {
          return { firstName: 'A first name is required' }
        }
        return undefined
      },
      onDynamic: ({ value }) => {
        if (!value.lastName) {
          return { lastName: 'A last name is required' }
        }
        return undefined
      },
    },
  })

  formErrorMap = injectStore(this.form, (state) => state.errorMap)
}

与字段结合使用

您也可以像使用其他验证逻辑一样,将 onDynamic 验证与字段一起使用。

angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
import type { FieldValidateFn } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <form (submit)="handleSubmit($event)">
      <ng-container
        [tanstackField]="form"
        name="age"
        [validators]="{
          onDynamic: ageValidator
        }"
        #age="field"
      >
        <input
          type="number"
          [value]="age.api.state.value"
          (blur)="age.api.handleBlur()"
          (input)="age.api.handleChange($any($event).target.valueAsNumber)"
        />
        @if (age.api.state.meta.errorMap.onDynamic) {
          <p style="color: red">
            {{ age.api.state.meta.errorMap.onDynamic }}
          </p>
        }
      </ng-container>
      <button type="submit">Submit</button>
    </form>
  `,
})
export class AppComponent {
  ageValidator: FieldValidateFn<any, any, any, any, number> = ({ value }) =>
    value > 18 ? undefined : 'Age must be greater than 18'

  form = injectForm({
    defaultValues: {
      name: '',
      age: 0,
    },
    validationLogic: revalidateLogic(),
    onSubmit({ value }) {
      alert(JSON.stringify(value))
    },
  })

  handleSubmit(event: SubmitEvent) {
    event.preventDefault()
    event.stopPropagation()
    this.form.handleSubmit()
  }
}
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
import type { FieldValidateFn } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <form (submit)="handleSubmit($event)">
      <ng-container
        [tanstackField]="form"
        name="age"
        [validators]="{
          onDynamic: ageValidator
        }"
        #age="field"
      >
        <input
          type="number"
          [value]="age.api.state.value"
          (blur)="age.api.handleBlur()"
          (input)="age.api.handleChange($any($event).target.valueAsNumber)"
        />
        @if (age.api.state.meta.errorMap.onDynamic) {
          <p style="color: red">
            {{ age.api.state.meta.errorMap.onDynamic }}
          </p>
        }
      </ng-container>
      <button type="submit">Submit</button>
    </form>
  `,
})
export class AppComponent {
  ageValidator: FieldValidateFn<any, any, any, any, number> = ({ value }) =>
    value > 18 ? undefined : 'Age must be greater than 18'

  form = injectForm({
    defaultValues: {
      name: '',
      age: 0,
    },
    validationLogic: revalidateLogic(),
    onSubmit({ value }) {
      alert(JSON.stringify(value))
    },
  })

  handleSubmit(event: SubmitEvent) {
    event.preventDefault()
    event.stopPropagation()
    this.form.handleSubmit()
  }
}

异步验证

异步验证也可以像其他验证逻辑一样与 onDynamic 一起使用。您甚至可以对异步验证进行防抖处理,以避免过多的调用。

angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <!-- Your form template here -->
  `,
})
export class AppComponent {
  form = injectForm({
    defaultValues: {
      username: '',
    },
    validationLogic: revalidateLogic(),
    validators: {
      onDynamicAsyncDebounceMs: 500, // Debounce the async validation by 500ms
      onDynamicAsync: async ({ value }) => {
        if (!value.username) {
          return { username: 'Username is required' }
        }
        // Simulate an async validation
        const isValid = await validateUsername(value.username)
        return isValid ? undefined : { username: 'Username is already taken' }
      },
    },
  })
}
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <!-- Your form template here -->
  `,
})
export class AppComponent {
  form = injectForm({
    defaultValues: {
      username: '',
    },
    validationLogic: revalidateLogic(),
    validators: {
      onDynamicAsyncDebounceMs: 500, // Debounce the async validation by 500ms
      onDynamicAsync: async ({ value }) => {
        if (!value.username) {
          return { username: 'Username is required' }
        }
        // Simulate an async validation
        const isValid = await validateUsername(value.username)
        return isValid ? undefined : { username: 'Username is already taken' }
      },
    },
  })
}

标准模式验证

您还可以将 Valibot 或 Zod 等标准模式验证库与 onDynamic 验证结合使用。这使您可以定义复杂而动态的验证规则,这些规则可以根据表单状态进行更改。

angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
import { z } from 'zod'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <!-- Your form template here -->
  `,
})
export class AppComponent {
  schema = z.object({
    firstName: z.string().min(1, 'A first name is required'),
    lastName: z.string().min(1, 'A last name is required'),
  })

  form = injectForm({
    defaultValues: {
      firstName: '',
      lastName: '',
    },
    validationLogic: revalidateLogic(),
    validators: {
      onDynamic: this.schema,
    },
  })
}
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
import { z } from 'zod'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TanStackField],
  template: `
    <!-- Your form template here -->
  `,
})
export class AppComponent {
  schema = z.object({
    firstName: z.string().min(1, 'A first name is required'),
    lastName: z.string().min(1, 'A last name is required'),
  })

  form = injectForm({
    defaultValues: {
      firstName: '',
      lastName: '',
    },
    validationLogic: revalidateLogic(),
    validators: {
      onDynamic: this.schema,
    },
  })
}
我们的合作伙伴
Code Rabbit
订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。

订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。