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

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

我正在尝试为我的 FormControl 制作自定义验证器

mealType

如果我的 FormControl

category
有值而
mealType
没有,则
mealType
应该无效。

如果

category
没有值,则
mealType
应该有效。

我收到控制台错误:

类型错误:无法读取未定义的属性“get”

代码:

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
    }
}

但它会导致:

错误 应用程序/find-page/subcomponents/find-page/find-form.component.html:36:5 原因:无法读取未定义的属性“值”

尝试这个:

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,
    });
}

但我有编译错误。我怎样才能做到这一点?我认为我对我所做的验证类和它的使用都有点偏离。

angular angular2-forms
4个回答
24
投票

你又近了一步。

您需要将自定义验证器附加到

FormGroup
,因为它需要知道两个
FormControl
categories
mealTypes
),因此附加到
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
,因此您可以在参数中附加自定义验证。另外,将自定义验证器移到组件类之外。

看看这个 Plunker 以获得有关您的具体案例的更多见解。


11
投票

您可以通过该控件及其父表单组导航到另一个控件:

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("")
    //..
});

注释: 进行跨字段验证的正确方法是在表单组级别使用自定义验证器。在这种情况下,生成的错误也会出现在同一级别上。现在,如果您希望为特定控件分配错误,您可以使用我上面的建议,但您需要记住,每次感兴趣的控件的值发生变化时,您都必须手动更新它们的值和有效性,如果您使用默认的“更改时更新”策略。 (因为一旦更新依赖的控件,控件的有效性可能会改变,而 Angular 只更新值发生变化的控件)


5
投票

@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,他创建了这篇文章。


3
投票

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

  • 您不能在 builder 子句中引用 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;
    };
}
© www.soinside.com 2019 - 2024. All rights reserved.