Angular 2条件Validators.required?

时间:2016-03-20 19:52:42

标签: angular

我应该如何有条件地要求表格字段?我创建了一个自定义验证器,但是我传递给自定义验证器的条件变量是静态的并保持其初始值。我的自定义验证器应该如何获得更新的条件值?也许有一种方法可以使用Validators.required而不是自定义验证器来执行此操作?

private foo: boolean = false;
private bar: boolean = true;

constructor(private _fb: FormBuilder) {
    function conditionalRequired(...conditions: boolean[]) {
      return (control: Control): { [s: string]: boolean } => {
        let required: boolean = true;
        for (var i = 0; i < conditions.length; i++) {
          if (conditions[i] === false) {
            required = false;
          }
        }
        if (required && !control.value) {
          return { required: true }
        }
      }
    }
    this.applyForm = _fb.group({
          'firstName': ['', Validators.compose([
            conditionalRequired(this.foo, !this.bar)
          ])],
          ...
    });
}

更新(2016年5月17日)

发布此内容已经过了很长时间,但我想引用.include()课程中提供的.exclude()ControlGroup方法,供那些试图创建的人使用这个功能。 (docs)虽然上面有条件验证器可能有用例,但我发现包含和排除控件,控件组和控件数组是处理这个问题的好方法。只需在您想要的控件上设置required验证器,并根据需要包含/排除它。希望这有助于某人!

5 个答案:

答案 0 :(得分:17)

我想要一个更通用的版本,所以我为它编写了一个额外的验证器,可以与其他验证器一起编写。我仍然只是开始查看表单模块,所以不要指望这是最有效的代码或在边缘情况下工作,但它是一个良好的开端。它适用于正常使用情况,可能是其他人的良好起点。

为rc.4和新表单模块制作,revalidateOnChanges部分可能很糟糕(不确定导致此行为的最佳方法),使用风险自负! :)

如何使用

验证器接受两个参数,一个给定formGroup的条件函数,如果要应用验证则返回true,否则返回false,以及验证器(可能是一个组合)。它会在formGroup更新时重新验证字段,并且它当前只能检查同一formGroup中的内容,但这应该很容易修复。

this.formBuilder.group({
    vehicleType: ['', Validators.required],
    licencePlate: [
        '',
        ExtraValidators.conditional(
            group => group.controls.vehicleType.value === 'car',
            Validators.compose([
                Validators.required,
                Validators.minLength(6)
            ])
        ),
    ]
});

在此示例中,您有两个字段,vehicleType和licencePlate。如果vehicleType为“car”,则条件语句将应用组合验证器(required和minLength)。

您可以使用compose应用可能同时适用或不适用的多个不同条件。这是一个稍微复杂的例子:

this.formBuilder.group({
    country: ['', Validators.required],
    vehicleType: ['', Validators.required],
    licencePlate: [
        '',
        Validators.compose([
            ExtraValidators.conditional(
                group => group.controls.vehicleType.value === 'car',
                Validators.required
            ),
            ExtraValidators.conditional(
                group => group.controls.country.value === 'sweden',
                Validators.minLength(6)
            ),
        ])
    ]
});

在这种情况下,如果类型是“汽车”,我们申请必填,如果国家是“瑞典”,我们申请minLength。如果只有一个条件适用,则仅适用验证,如果两个条件均适用,则两个验证均适用。

验证器本身

请注意,对象比较只是一个简单的暴力,因为我们正在使用小对象,如果你使用的是Ramda或者你可以削减大量代码的东西。

export class ExtraValidators {
    static conditional(conditional, validator) {
        return function(control) {
            revalidateOnChanges(control);

            if (control && control._parent) {
                if (conditional(control._parent)) {
                    return validator(control);
                }
            }
        };
    }
}
function revalidateOnChanges(control): void {
    if (control && control._parent && !control._revalidateOnChanges) {
        control._revalidateOnChanges = true;
        control._parent
            .valueChanges
            .distinctUntilChanged((a, b) => {
                // These will always be plain objects coming from the form, do a simple comparison
                if(a && !b || !a && b) {
                    return false;
                } else if (a && b && Object.keys(a).length !== Object.keys(b).length) {
                    return false;
                } else if (a && b) {
                    for (let i in a) {
                        if(a[i] !== b[i]) {
                            return false;
                        }
                    }
                }
                return true;
            })
            .subscribe(() => {
                control.updateValueAndValidity();
            });

        control.updateValueAndValidity();
    }
    return;
}

注意:记得导入运营商:
import'rxjs / add / operator / distinctUntilChanged';

答案 1 :(得分:9)

根据您的评论,我可以看到潜在的问题。由于您为创建验证器函数的函数提供了条件作为基本类型,因此将使用调用第一个函数时的值。即使他们之后改变,也不会考虑新的价值。

要归档您需要将对象用于以下所述的条件:

private foo: boolean = false;
private bar: boolean = true;

private conditions: any = {
  condition1: foo,
  condition2: !bar
};

constructor(private _fb: FormBuilder) {
    function conditionalRequired(conditions: any) {
      return (control: Control): { [s: string]: boolean } => {
        let required: boolean = true;
        for (var elt in conditions) {
          var condition = conditions[elt];
          if (conditions === false) {
            required = false;
          }
        }
        if (required && !control.value) {
          return { required: true };
        }
      }
    }
    this.applyForm = _fb.group({
          'firstName': ['', Validators.compose([
            conditionalRequired(conditions)
          ])],
          ...
    });
}

这样可以通过引用使用/更新conditions参数。要更新您的条件,您需要执行以下操作:

updateConditions() {
  this.conditions.condition1 = true;
  this.conditions.condition2 = true;
}

这是一个傻瓜:https://plnkr.co/edit/bnX7p0?p=preview

修改

要在更新条件时运行验证程序,您需要显式调用控件的updateValueAndValidity方法。在这种情况下,控件和表单的valid属性将相应更新:

updateConditions() {
  this.conditions.condition1 = true;
  this.conditions.condition2 = true;
  this.applyForm.controls.firstName.updateValueAndValidity();
}

答案 2 :(得分:6)

我们可以使用setValidators()方法在找到控件后设置所需的动态验证 -

this.applyForm.get('firstName').setValidators(setRequired());

setRequired() {
        if(1==1) {
            return [Validators.required];
        } else {
            return [];
        }   
    }

答案 3 :(得分:3)

我创建了一个接受函数回调的验证器,它允许我使验证器可重用,但我似乎无法找到一个好的方法来确保我使用此验证器在控件上调用updateValueAndValidity()而无需手动当其他控件的值发生变化时调用它。

<强>验证

export class ValidationService {
    static conditionalRequired = (isRequiredFunction: Function) => {
        return (control: Control) => {
           if (!control.value && isRequiredFunction())
           ...

组件页

private myControl1: Control = 
    new Control("", ValidationService.conditionalRequired(() => 
        { return this && this.model && this.model.SomeProperty === 'SomeValue' } ));

private myControl2: Control = 
    new Control("", 
    ValidationService.conditionalRequired(this.isControl2Required.bind(this)));

isControl2Required() { 
    return someCondition;
}

答案 4 :(得分:0)

只需在enable()上使用disable()AbstractControl方法。

this.myControl.valueChanges.subscribe(
      checked => {
        if (checked) {
          this.otherControl.enable();
        } else {
          this.otherControl.disable();
        }
      }
    );