多个字段上的角度自定义验证器

时间:2019-06-22 14:19:48

标签: angular

我有以下代码片段,用于以Angular模板驱动形式验证密码和再次密码字段。这样的想法是,如果两个字段不同,则会在两个字段上附加一个错误。如果它们相同,则不会从两者中删除任何错误。

validate(control: AbstractControl): ValidationErrors | null {

    // The current value
    let currentValue = control.value;

    // Control.root exposes the parent element of the directory
    let otherField : AbstractControl = control.root.get(this.compareTo);


    if(currentValue && otherField && currentValue !== otherField.value) {

      otherField.setErrors({"fieldsMismatch": true});
      otherField.markAsTouched();

      setTimeout(() => {
        control.setErrors({"fieldsMismatch" : true});
        control.markAsTouched()}
        )


    } else {
      control.setErrors(null)
      otherField.setErrors(null)
    }


    return null;
  }

1)如果我从setTimeout中删除了设置当前元素(控制)错误的逻辑,它将停止工作。为什么?

2)如果我使用清除错误

control.setError({"fieldsMismatch": null});
otherField.setError({"fieldsMismatch": null});

该错误已同时消除。但是,对于当前字段(控件),错误键设置为null,这意味着.ng-invalid已从输入标签中删除。对于otherField,将错误设置为空对象,这意味着输入仍被标记为无效。为什么?我可以将错误显式设置为null,但是如果我还有其他验证也可以将其删除。

两个对象的类型均为AbstractControl,所以我不知道是什么引起了差异。

2 个答案:

答案 0 :(得分:0)

一种更好的方法是创建一个表单组级别,以比较在您访问该表单组时在该级别上的两个字段,并设置错误以自行形成表单

检查此验证器

export function checkMatchValidator(field1: string, field2: string) {
  return function (frm) {
    let field1Value = frm.get(field1).value;
    let field2Value = frm.get(field2).value;

    if (field1Value !== '' && field1Value !== field2Value) {
      return { 'match': `value ${field1Value} is not equal to ${field2Value}` }
    }
    return null;
  }
}

密码/ confirmPassword表单

 form: FormGroup
  constructor(formBuilder: FormBuilder) {

    this.form = formBuilder.group({
      password: ['', Validators.required],
      confirmPassword: ['', Validators.required],
    },
      {
        validator: checkMatchValidator('password', 'confirmPassword')
      }
    );
  }
}

基于表单状态的样式无效输入

.login-form.ng-invalid.ng-touched input {
  border:1px solid red;
  background:rgba(255,0,0,0.2);
}

答案 1 :(得分:0)

在有角度的模板驱动形式中,您可以使用实现验证器的指令(如果不需要传递参数,则不必要),请参见the docs

在这种情况下,我们可以使用类似

@Directive({
  selector: '[repeatControl]',
  providers: [{ provide: NG_VALIDATORS, useExisting: RepeatNameDirective, multi: true }]
})
export class RepeatNameDirective implements Validator {
  @Input('repeatControl') control: NgControl;

  validate(control: AbstractControl): { [key: string]: any } | null {
    let invalid: boolean = this.control.value && control.value && 
            this.control.value != control.value;

    if (this.control.invalid != invalid)
      setTimeout(() => {
        this.control.control.updateValueAndValidity({ emitEvent: false })
      })

    return invalid ? { error: "Must be equal" }
      : null;
  }
}

看到我们需要将要比较的控件传递给参数,以允许我们进行updateValueAndValidity(否则一个控件将是无效的,而另一个控件将是无效的,反之亦然),并且需要将该指令置于setTimeOut下

我们的表格变成

<input id="password" name="password" class="form-control"
      [repeatControl]="repeatPassword"
      [(ngModel)]="data.password" #password="ngModel"  >

<input id="repeatPassword" name="repeatPassword" class="form-control"
      [repeatControl]="password"
      [(ngModel)]="data.repeatPassword" #repeatPassword="ngModel" >

<div *ngIf="repeatPassword.errors || password.errors">
  Password and repeat password must be equals
</div>

请确保输入为referenceVariable 请参阅stackblitz(并考虑Angular如何将ng-invalid类自动添加到两个输入中)