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

标签 angular typescript validation ionic4 angular-forms

场景:

最初我有一个文本框(Name1)、一个日期选择器(DOB1)和一个复选框(Compare)。两者 Name1DOB1是必须的。单击复选框时,将动态添加另外两个名为 Name2 的表单控件和 DOB2Name1 中的任何一个或 DOB2是必需的。

所以有效的形式是有任何

  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>

时间:

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

最佳答案

这是因为 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
});

关于angular - 根据条件动态添加/删除验证器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57038703/

相关文章:

javascript - d3.js:在 Angular 应用程序和 node.js 上运行相同的代码

javascript - 在 javascript/typescript 中将 Element 转换为 HTMLElement

asp.net-mvc - ASP.NET MVC 验证后显示 div

javascript - 隐藏/显示表单输入和更改值

ruby-on-rails-3 - 如何为验证数字子选项自定义错误消息?

html - 如何创建带有 Angular 数组的表行?

html - 将子组件变量与父组件 Angular 4 中的下拉列表绑定(bind)

javascript - 尝试删除单个子项时整个 Firebase 数据库被删除

typescript - 如何从 TypeScript 中的记录类型中提取 varargs 参数?

typescript - 如何在Typescript中获取axios响应头