FormBuilder 组已弃用

FormBuilder group is deprecated

我将我的项目迁移到 angular 11,我注意到我添加的全局验证使 FormBuilder.group 已弃用并显示以下消息:

group is deprecated: This api is not typesafe and can result in issues with Closure Compiler renaming.
Use the `FormBuilder#group` overload with `AbstractControlOptions` instead.

所以这已被弃用:

  ingredientForm = this.fb.group({
    ingredientType: ['', Validators.required],
    ingredientFlavor: [''],
    isMultiFlavor: [''],
    ingredientBrand: [''],
    ingredientName: [''],
    imageFile: ['']
  }, {validators: [ValidateThirdNumber.validate]});

没有 validators 选项就不是。

我的 ValidateThirdNumber 验证者:

class ValidateThirdNumber {
  static validate(control: AbstractControl): void {
      if (control) {
      const isMultiFlavor = control.get('isMultiFlavor')?.value;
      const ingredientFlavor = control.get('ingredientFlavor')?.value;
      const ingredientBrand = control.get('ingredientBrand')?.value;
      const ingredientName = control.get('ingredientName')?.value;
      if (isMultiFlavor && ingredientFlavor.trim().length === 0) {
        control.get('ingredientFlavor')?.setErrors({required_if: true});
      } else {
        control.get('ingredientFlavor')?.setErrors(null);
      }
      if (!ingredientFlavor && !ingredientBrand && !ingredientName) {
        control.get('ingredientName')?.setErrors({required_at_least: true});
        control.get('ingredientFlavor')?.setErrors({required_at_least: true});
        control.get('ingredientBrand')?.setErrors({required_at_least: true});
      } else {
        control.get('ingredientName')?.setErrors(null);
        control.get('ingredientFlavor')?.setErrors(null);
        control.get('ingredientBrand')?.setErrors(null);
      }
      if (ingredientBrand && ingredientName && ingredientName === ingredientBrand) {
        control.get('ingredientName')?.setErrors({not_the_same: true});
        control.get('ingredientBrand')?.setErrors({not_the_same: true});
      }
    }
  }
}

如何使用 AbstractControlOptions 重载它?

问题描述

documentation 我们看到两个不同的行与 group() 函数

group(controlsConfig: { [key: string]: any; }, options?: AbstractControlOptions): FormGroup

AND

group(controlsConfig: { [key: string]: any; }, options: { [key: string]: any; }): FormGroup

第二个定义已弃用

这几行的区别是options?: AbstractControlOptionsoptions: { [key: string]: any; }

要理解 angular 抛出此错误的原因,我们现在将考虑 AbstractControlOptions

interface AbstractControlOptions {
  validators?: ValidatorFn | ValidatorFn[] | null
  asyncValidators?: AsyncValidatorFn | AsyncValidatorFn[] | null
  updateOn?: 'change' | 'blur' | 'submit'
}

我们继续分解问题,注意到这个结构和你的结构之间的区别是ValidatorFn[]

interface ValidatorFn {
  (control: AbstractControl): ValidationErrors | null
}

总的来说,在您的情况下会引发错误,因为您的 Validator 函数应该控制 return ValidationErrors | null。在行 validate(control: AbstractControl): void 中,您的代码实际上 returns void 但预期 return a ValidationError | null

解决方案

从问题描述来看,解决方法是简单修改ValidatorFn

确保您的 ValidatorFn return 是 ValidationError 或者如果没有错误 return 是 null 来自 ValidationErrors defination

type ValidationErrors = {
    [key: string]: any;
};

您将需要 return 键值对对象,例如 {required_if: true}

我们可以按预期添加 return 语句来更改您的代码

class ValidateThirdNumber {
  static validate(control: AbstractControl): ValidationErrors | null {
      if (control) {
      const isMultiFlavor = control.get('isMultiFlavor')?.value;
      const ingredientFlavor = control.get('ingredientFlavor')?.value;
      const ingredientBrand = control.get('ingredientBrand')?.value;
      const ingredientName = control.get('ingredientName')?.value;
      if (isMultiFlavor && ingredientFlavor.trim().length === 0) {
        control.get('ingredientFlavor')?.setErrors({required_if: true});
        return ({required_if: true});
      } else {
        control.get('ingredientFlavor')?.setErrors(null);
      }
      if (!ingredientFlavor && !ingredientBrand && !ingredientName) {
        control.get('ingredientName')?.setErrors({required_at_least: true});
        control.get('ingredientFlavor')?.setErrors({required_at_least: true});
        control.get('ingredientBrand')?.setErrors({required_at_least: true});
        return ({required_at_least: true});
      } else {
        control.get('ingredientName')?.setErrors(null);
        control.get('ingredientFlavor')?.setErrors(null);
        control.get('ingredientBrand')?.setErrors(null);
      }
      if (ingredientBrand && ingredientName && ingredientName === ingredientBrand) {
        control.get('ingredientName')?.setErrors({not_the_same: true});
        control.get('ingredientBrand')?.setErrors({not_the_same: true});
        return ({not_the_same: true});
      }
    }
    return null;
  }
}

我也收到同样的错误,我做了以下更改。

  • 确保您的验证器函数签名与此匹配。 (一个接收控件并同步 returns 验证错误映射(如果存在)的函数,否则为空。)

    • 函数Your_Function_Name(对象名称:AbstractControl):ValidationErrors |无效的 { }
  • 您可以像这样更改表单生成器中的代码。

    • const formOptions: AbstractControlOptions = { validators: Your_Function_Name };
  • 并在 formbuilder 对象中像这样在 formOptions 对象上方传递

    • this.formObject = this.formBuilder.group({ 全名:['', [Validators.required]] }, 表单选项);

虽然 的答案解释完整,但我想添加一个不同的答案,指出要点:

  1. 指定验证器函数时,只指定函数名(,不带任何参数)
  2. 该验证器函数必须接受一个且仅一个 AbstractControl
  3. 类型的参数
  4. 验证器函数必须 return {key: value}null,但 而不是 始终 null。 Return null 当没有错误显示时。

这是在 RegisterPage.ts 中声明的表单示例:

import { Component, OnInit } from '@angular/core';

import {
  AbstractControl,
  FormBuilder,
  FormControl,
  FormGroup,
  Validators,
} from '@angular/forms';

@Component({
  selector: 'app-register',
  templateUrl: './register.page.html',
  styleUrls: ['./register.page.scss'],
})
export class RegisterPage implements OnInit {
  form: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.createForm();
  }

  createForm() {
    this.form = this.fb.group({
      matchEmail: this.fb.group(
        {
          //email textfield
          email: new FormControl(
            '',
            Validators.compose([
              Validators.required,
              Validators.maxLength(64),
              Validators.email,
            ])
          ),
          //confirm email textfield
          confirmEmail: new FormControl(''),
        },
        { validators: this.matchEmail }
      ),
    });
  }
}

验证器函数将创建一个名为 mismatch 的新错误类型,可以插入到 RegisterPage.ts 文件中:

matchEmail(group: AbstractControl) {
    let email: string = group.get('email').value;
    let confirmEmail: string = group.get('confirmEmail').value;
    if (email.localeCompare(confirmEmail) != 0) {
        //error
        group.get('confirmEmail')?.setErrors({ mismatch: true });
        return { mismatch: true };
    } else {
        //no error, return null (don't return { mismatch: false })
        return null;
    }
}

你的 html 可以是这样的(你不能直接访问 confirmEmail,它必须通过它所属的组 matchEmail.confirmEmail):

<div *ngIf="form.get('matchEmail.confirmEmail').hasError('mismatch')
    && (form.get('matchEmail.confirmEmail').dirty ||
    form.get('matchEmail.confirmEmail').touched)">
    <small>Confirmation email doesn't match</small>
</div>