Angular 2 依赖于另一个表单控件的自定义验证器

Angular 2 Custom validator that depends on another form control

我正在尝试为我的 FormControl 创建一个自定义验证器 mealType

如果我的 FormControl category 有值而 mealType 没有,那么 mealType 应该是无效的。

如果category没有值,mealType应该有效。

我收到控制台错误:

TypeError: Cannot read property 'get' of undefined

代码:

ngOnInit() {
    this.findForm = this.formBuilder.group({
        categories: [null, Validators.required],
        mealTypes: [null, this.validateMealType],
        distanceNumber: null,
        distanceUnit: 'kilometers',
        keywords: null,
    });
}

validateMealType() {
    if (this.findForm.get('categories').value) {
        if (this.findForm.get('mealTypes').value) {
            var mealTypeError = false;
        } else {
            var mealTypeError = true;
        }
    } else {
        var mealTypeError = false;
    }

    return mealTypeError ? null : {
        error: true
    }
}

这是我的表单未定义。

我该如何解决这个问题?

试试这个:

validateMealType(categoryControl: FormControl, mealTypeControl: FormControl) {
    if (categoryControl.value) {
        if (!mealTypeControl.value) {
            var mealTypeError = true;
        } else {
            var mealTypeError = false;
        }
    } else {
        var mealTypeError = false;
    }

    return mealTypeError ? null : {
        error: true
    }
}

但它导致:

Error in app/find-page/subcomponents/find-page/find-form.component.html:36:5 caused by: Cannot read property 'value' of undefined

试试这个:

class MealTypeValidator {

    constructor(private categoryFormControl: FormControl) { }

    mealTypeValidator(control: FormControl): { [error: string]: any } {
        if (this.categoryFormControl.value) {
            if (!control.value) {
                return { error: true };
            }
        }
    }
}

然后在我的表单组件中:

ngOnInit() {
    this.findForm = this.formBuilder.group({
        categories: [null, Validators.required],
        mealTypes: [null, new MealTypeValidator(this.findForm.get('categories').mealTypeValidator()],
        distanceNumber: null,
        distanceUnit: 'kilometers',
        keywords: null,
    });
}

但是我有编译错误。我该如何做对?我认为我对我所做的验证 class 和它的使用都有点不满意。

你更近了一步。

您需要将自定义验证器附加到 FormGroup,因为它需要知道两个 FormControlcategoriesmealTypes),因此附加到 FormGroup 将为验证者提供更广阔的视野和访问整个 FormControl

为此,请将您的 ngOnInit 更改为

ngOnInit() {
    this.findForm = new FormGroup({
        mealTypes : new FormControl(null, Validators.Required),
        categories : new FormControl(null)
        // others form control here
    }, validateMealType); // <-- see here is your custom function
}

在上面的代码中,您实际上必须使用 FormGroup 构造函数而不是 FormBuilder,因此您可以在参数中附加自定义验证。此外,将您的自定义验证器移到组件 class 之外。

在此处查看此 Plunker 以更深入地了解您的具体案例。

@Michael 提出的解决方案对我有用 Angular 4.

在验证函数中,我需要将参数类型从AbstractControl更改为FormGroup,因为这个版本的AbstractControl不包含控件集合。

function validateEqual(form: FormGroup): { [key: string]: boolean } {
  const senha = form.controls['novaSenha'];
  const confirmacaoSenha = form.controls['confirmacaoNovaSenha'];

  if (senha != undefined && confirmacaoSenha != undefined) {
    const senhaValue = senha.value;
    const confirmacaoSenhaValue = confirmacaoSenha.value;

    if (senhaValue !== confirmacaoSenhaValue) {
        return { 'A senha e a confirmação não coincidem.': true};
    }

    return null;
  }
}

也感谢@Ariel,他发现了这个post。

您可以通过控件及其父窗体组导航到另一个控件:

example(): ValidatorFn {
     return (control: AbstractControl): ValidationErrors | null => {
         const forbidden = control.value < control.parent.controls["anotherControl"].value;
         return forbidden ? { forbidden: { message: "Custom message" } } : null;
     };
}

将以上内容作为函数添加到您的组件中,并将此验证器声明到您的表单控件中:

formGroup = new FormGroup({
    //..
    targetControl: new FormControl("", [this.example()]),
    anotherControl: new FormControl("")
    //..
});

mealTypes: [null, new MealTypeValidator(this.findForm.get('categories').mealTypeValidator()]

  • 您不能在构建器子句中引用 formGroup。

但是我们可以从控件中获取父级并使用可选运算符我们可以引用值并获取另一个控件值然后比较我们需要的任何逻辑。

ngOnInit() {
    this.findForm = this.formBuilder.group({
        categories: [null, Validators.required],
        mealTypes: [null, this.mealTypeValidator()],
        distanceNumber: null,
        distanceUnit: 'kilometers',
        keywords: null,
    });
}

mealTypeValidator(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
        return control.parent?.value.categories && !control.value
            ? { forbidden: { message: 'MealType should have a type under category!' } }
            : null;
    };
}