为 Angular Reactive Forms 交叉属性验证器声明正确的类型?

问题描述 投票:0回答:2

本教程中,作者实现了一个密码检查验证器,如下所示:

  MatchPassword(password: string, confirmPassword: string) {
    return (formGroup: FormGroup) => {
      const passwordControl = formGroup.controls[password];
      const confirmPasswordControl = formGroup.controls[confirmPassword];

      if (!passwordControl || !confirmPasswordControl) {
        return null;
      }

      if (confirmPasswordControl.errors && !confirmPasswordControl.errors.passwordMismatch) {
        return null;
      }

      if (passwordControl.value !== confirmPasswordControl.value) {
        confirmPasswordControl.setErrors({ passwordMismatch: true });
      } else {
        confirmPasswordControl.setErrors(null);
      }
    }
  }

函数没有设置返回类型。这会导致 Angular 13 中出现 linting 错误。

我尝试通过将交叉属性验证函数的签名更改为以下方式来修复 linting 错误:

   MatchPassword(password: string, confirmPassword: string):ValidatorFn | ValidatorFn[] | AbstractControlOptions | null | undefined { ...

但是现在返回的验证函数签名上出现 linter 错误:

...
     return (formGroup: FormGroup):ValidatorFn | ValidatorFn[] | AbstractControlOptions | null | undefined => {
...
Type '(formGroup: FormGroup) => ValidatorFn | ValidatorFn[] | AbstractControlOptions | null | undefined' is not assignable to type 'ValidatorFn | AbstractControlOptions | ValidatorFn[] | null | undefined'.
  Type '(formGroup: FormGroup) => ValidatorFn | ValidatorFn[] | AbstractControlOptions | null | undefined' is not assignable to type 'ValidatorFn'.
    Types of parameters 'formGroup' and 'control' are incompatible.
      Type 'AbstractControl' is missing the following properties from type 'FormGroup': controls, registerControl, addControl, removeControl, and 3 more.ts(2322)

这不会发生在 Stackblitz 上,只会发生在新的 Angular 13 项目上,因此我无法为其创建 Stackblitz。

有人知道如何声明执行跨属性验证的反应式表单验证器的签名吗?

linting 进展

对于那些想知道的人来说,第一个 linting 错误发生在分配交叉属性验证器的注册组件中。

它创建

FormGroup
实例,并将自定义交叉属性验证器指定为第三个参数,如下所示:

new FormGroup('', ..., this.v. MatchPassword('password', 'confirmPassword'))

这就是第一个 linting 错误发生的地方。

** 旁注 **

this.v
是包含
MatchPassword
函数的注入验证服务。

生成的 linting 错误说明如下:

Argument of type '(formGroup: FormGroup) => null | undefined' is not assignable to parameter of type 'ValidatorFn | ValidatorFn[] | AbstractControlOptions | null | undefined'.
  Type '(formGroup: FormGroup) => null | undefined' is not assignable to type 'ValidatorFn'.
    Types of parameters 'formGroup' and 'control' are incompatible.
      Type 'AbstractControl' is missing the following properties from type 'FormGroup': controls, registerControl, addControl, removeControl, and 3 more.ts(2345)
javascript angular typescript angular-reactive-forms
2个回答
0
投票

如果您想进行跨字段验证,这很容易,因为当传递到

new FormGroup(...)
时,验证器会获取实际的
FormGroup
。但对于打字 Angular 有
(control: AbstractControl)
这很好,因为
FormGroup
实际上是
AbstractControl
的一种。所以您需要做的就是检查这是一个
FormGroup
而不是其他类型的
AbstractControl
之一,这样您就可以做

   MatchPassword(password: string, confirmPassword: string) {
     return (control: AbstractControl) => {
         const isFormGroup = control instanceof FormGroup;
         if (!isFormGroup) {
            return null
         }
        // do what ever you'd like...
        const passwordControl = formGroup.controls[password];
        const confirmPasswordControl = formGroup.controls[confirmPassword];
  

-1
投票

好的,根据 Angular 团队的意见,我们将验证器的接口更改为预期的接口,并相应地更改了实现。

关键是我们可以使用

AbstractControl.get
来获取表单上的所有控件,这样我们就可以使用
ValidatorFn
界面了。

这是新的实现:

   passwordMatch(password: string, confirmPassword: string):ValidatorFn {
    return (formGroup: AbstractControl):{ [key: string]: any } | null => {
      const passwordControl = formGroup.get(password);
      const confirmPasswordControl = formGroup.get(confirmPassword);
      
      if (!passwordControl || !confirmPasswordControl) {
        return null;
      }

      if (
        confirmPasswordControl.errors &&
        !confirmPasswordControl.errors.passwordMismatch
      ) {
        return null;
      }

      if (passwordControl.value !== confirmPasswordControl.value) {
        confirmPasswordControl.setErrors({ passwordMismatch: true });
        return { passwordMismatch: true }
      } else {
        confirmPasswordControl.setErrors(null);
        return null;
      }
    };
  }
© www.soinside.com 2019 - 2024. All rights reserved.