根据条件动态添加/删除验证器

时间:2019-07-15 11:24:56

标签: angular typescript validation ionic4 angular-forms

场景:

最初,我有一个文本框(Name1),一个日期选择器(DOB1)和一个复选框(Compare)。 Name1DOB1都是必需的。单击复选框后,将动态添加两个名为Name2DOB2的表单控件,并且需要Name1DOB2中的任何一个。

因此有效表格具有

中的任何一个
  1. Name1 DOB1 Name2或//如果Name2有效,则需要从DOB2中删除所需的验证器
  2. Name1 DOB1 DOB2或//如果DOB2有效,则需要从Name2中删除所需的验证器
  3. Name1 DOB1 Name2 DOB2

在上述所有情况下,该表单均有效,显示启用提交按钮。

问题:

我曾尝试使用setValidators,但仍无法弄清我所缺少的内容。当我单击复选框时,该表单仅在所有四个控件均有效时才有效。我只需要其中三个有效即可。

代码:

<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
  <ion-card class="person1">
    <ion-card-content>
      <ion-list lines="full" class="ion-no-margin ion-no-padding">
        <ion-item>
          <ion-label position="stacked">Name / Number <ion-text color="danger">*</ion-text>
          </ion-label>
          <ion-input type="text" formControlName="NameNumber"></ion-input>
        </ion-item>
        <ion-item>
          <ion-label position="stacked">Date of birth<ion-text color="danger">*</ion-text>
          </ion-label>
          <ion-datetime required placeholder="Select Date" formControlName="DateOfBirth"></ion-datetime>
        </ion-item>
      </ion-list>
    </ion-card-content>
  </ion-card>
  <ion-card class="person2" *ngIf="isComparisonChecked">
    <ion-card-content>
      <ion-list lines="full" class="ion-no-margin ion-no-padding">
        <ion-item>
          <ion-label position="stacked">Name / Number <ion-text color="danger">*</ion-text>
          </ion-label>
          <ion-input type="text" formControlName="NameNumber2"></ion-input>
        </ion-item>
        <ion-item>
          <ion-label position="stacked">Date of birth<ion-text color="danger">*</ion-text>
          </ion-label>
          <ion-datetime required placeholder="Select Date" formControlName="DateOfBirth2"></ion-datetime>
        </ion-item>
      </ion-list>
    </ion-card-content>
  </ion-card>
  <ion-item class="compare-section" lines="none">
    <ion-label>Compare</ion-label>
    <ion-checkbox color="danger" formControlName="IsCompare"></ion-checkbox>
  </ion-item>
  <div class="ion-padding">
    <ion-button color="danger" *ngIf="LicensedStatus" [disabled]="!this.profileForm.valid" expand="block"
      type="submit" class="ion-no-margin">Submit</ion-button>
  </div>
</form>

Ts:

profileForm = new FormGroup({
NameNumber: new FormControl('', [Validators.required, Validators.pattern('^[A-Za-z0-9 _]*[A-Za-z0-9][A-Za-z0-9 _]*$')]),
DateOfBirth: new FormControl('', Validators.required),
IsCompare: new FormControl(false)
});
...
this.profileForm.get('IsCompare').valueChanges.subscribe(checked => {
if (checked) {
    this.profileForm.addControl('NameNumber2', new FormControl('', [Validators.required, Validators.pattern('^[A-Za-z0-9 _]*[A-Za-z0-9][A-Za-z0-9 _]*$')]));
    this.profileForm.addControl('DateOfBirth2', new FormControl('', Validators.required));

    this.profileForm.get('NameNumber2').valueChanges.subscribe(() => {
      if (this.profileForm.get('NameNumber2').valid) {
        this.profileForm.get('DateOfBirth2').clearValidators();
      }
      else {
        this.profileForm.get('DateOfBirth2').setValidators([Validators.required]);
      }
    this.profileForm.get('DateOfBirth2').updateValueAndValidity();
    });

    this.profileForm.get('DateOfBirth2').valueChanges.subscribe(() => {
      if (this.profileForm.get('DateOfBirth2').valid) {
        this.profileForm.get('NameNumber2').clearValidators();
      }
      else {
        this.profileForm.get('NameNumber2').setValidators([Validators.required, Validators.pattern('^[A-Za-z0-9 _]*[A-Za-z0-9][A-Za-z0-9 _]*$')]);
      }
    this.profileForm.get('NameNumber2').updateValueAndValidity();
    });
  }
  else {
    this.profileForm.removeControl('NameNumber2');
    this.profileForm.removeControl('DateOfBirth2');
  }
});

我在这里想念什么?

更新#1:

我已经更新了上面的代码。如果我使用updateValueAndValidity,则会在控制台中收到此错误

enter image description here

4 个答案:

答案 0 :(得分:1)

使用 rxjs / operators 中的 distinctUntilChanged 将解决最大调用堆栈大小超出错误。

将行更改为

this.profileForm.get('NameNumber2').valueChanges.subscribe(() => {

this.profileForm.get('NameNumber2').valueChanges.pipe(distinctUntilChanged()).subscribe(() => {

因此,总体代码如下所示。

import { distinctUntilChanged } from 'rxjs/operators';

this.profileForm.get('NameNumber2').valueChanges.pipe(distinctUntilChanged()).subscribe(() => {
     if (this.profileForm.get('NameNumber2').valid) {
        this.profileForm.get('DateOfBirth2').clearValidators();
     }
     else {
       this.profileForm.get('DateOfBirth2').setValidators([Validators.required]);
     }
     this.profileForm.get('DateOfBirth2').updateValueAndValidity();
  });

this.profileForm.get('DateOfBirth2').valueChanges.pipe(distinctUntilChanged()).subscribe(() => {
     if (this.profileForm.get('DateOfBirth2').valid) {
        this.profileForm.get('NameNumber2').clearValidators();
     }
     else {
        this.profileForm.get('NameNumber2').setValidators([Validators.required, Validators.pattern('^[A-Za-z0-9 _]*[A-Za-z0-9][A-Za-z0-9 _]*$')]);
     }
     this.profileForm.get('NameNumber2').updateValueAndValidity();
});

我运行了上面更改的代码,该表单有效,并且您提到的所有方案都启用了提交按钮。

答案 1 :(得分:1)

之所以发生这种情况,是因为updateValueAndValidity()发出了另一个valueChanges事件。因此,您的订阅可以彼此无限触发。

this.profileForm.get('NameNumber2').valueChanges.subscribe(() => {
  // omitted
  this.profileForm.get('DateOfBirth2').updateValueAndValidity(); // Triggers valueChanges for 'DateOfBirth2' 
});

this.profileForm.get('DateOfBirth2').valueChanges.subscribe(() => {
  // omitted
  this.profileForm.get('NameNumber2').updateValueAndValidity(); // Triggers valueChanges for 'NameNumber2' 
});

避免这种情况的一种方法已在先前的文章中介绍:使用distinctUntilChanged

不过,方法本身内置了一种更简洁的方法:updateValueAndValidity()使用一个对象来配置其行为。 updateValueAndValidity({emitEvent: false})将阻止发出valueChanges事件,从而停止事件循环。

this.profileForm.get('NameNumber2').valueChanges.subscribe(() => {
  // omitted
  this.profileForm.get('DateOfBirth2').updateValueAndValidity({emitEvent:false}); // Does NOT trigger valueChanges
});

this.profileForm.get('DateOfBirth2').valueChanges.subscribe(() => {
  // omitted
  this.profileForm.get('NameNumber2').updateValueAndValidity({emitEvent:false}); // Does NOT trigger valueChanges
});

答案 2 :(得分:0)

尝试以下代码。

this.profileForm.get('DateOfBirth2').setValidators([Validators.required]);
this.profileForm.get('DateOfBirth2').updateValueAndValidity();

答案 3 :(得分:0)

为什么不对所有表单使用customValidator?您发出差异错误,并检查表单错误。一个辅助功能指示您所有字段有错误 像这样:

$ doit dynamic -v 8

您的表单就像

  form=new FormGroup({
    name1:new FormControl(),
    date1:new FormControl(),
    compare:new FormControl(),
    name2:new FormControl(),
    date2:new FormControl(),
  },this.customValidator())

  hasError(error:string)
  {
    return this.form.errors?this.form.errors.error.find(x=>x==error):null
  }
  customValidator()
  {
    return (form:FormGroup)=>{
      const errors=[];
      if (!form.value.compare)
      {
        if (!form.value.name1)
            errors.push('name1')
        if (!form.value.date1)
            errors.push('date1')
      }
      else
      {
          ....
      }
      return errors.length?{error:errors}:null
    }
  }

另一个想法是相似的,有一个customValidator总是返回null,但是使用setErrors来给字段手动输入错误

<form [formGroup]="form">
  <input formControlName="name1"/>
  <span *ngIf="hasError('name1')">*</span>

  <input formControlName="date1"/>
  <span *ngIf="hasError('date1')">*</span>
  <br/>
  <input type="checkbox" formControlName="compare"/>
  <br/>
  <input *ngIf="form.get('compare').value" formControlName="name2"/>
  <span *ngIf="hasError('name2')">*</span>
  <input *ngIf="form.get('compare').value" formControlName="date2"/>
    <span *ngIf="hasError('date2')">*</span>
</form>