angular中關於表單動態驗證的一種新思路

myskies發表於2022-03-17

本文demo地址:原始碼最終效果

在專案中,我們有時候往往需要動表單的驗證做動態的規劃。比如在一個註冊介面中同步註冊兩種使用者,但兩種使用者的輸入項卻不是相同的。

教師的話,要求輸入工號:
image.png

學生使用者的話,則要求輸入學號:
image.png

我們把這種情景,稱為動態的表單驗證。

在上述表中校驗中,我們要求:

  1. 工號與學號互不干擾。
  2. 選擇教師型別時,只判斷工號是否已經輸入。
  3. 選擇學生型別時,則只判斷學號是否已經輸入。

實現方案

其實這個實現的方案有很多種。在專案中我們已經使用過的大體有三種:

  1. 使用跨欄位驗證器。
  2. 訂閱使用者型別,將使用者型別發生變化時,重置工號學號的驗證規則。
  3. 訂閱使用者型別,將使用者型別發生變化時,在fromGroup中新增或移除工號,學號FromControl。

跨欄位驗證器

Anguar的官方給出在在跨欄位驗證器的使用示例,該思想是在FromGroup上新增一個驗證器,然後在該驗證器中獲取FormControl的值,在根據具體的情況來進行驗證。

優點:

  1. 官方示例,學習成本低。
  2. 直接將驗證放到了驗證器中,邏輯清晰。
  3. 驗證器不會對獲取FromGroup的值產生影響。

缺點:

  1. 無法在FormControl直接定義驗證條件,不直觀。
  2. 只能統一顯示錯誤資訊,無法為單一的欄位定製錯誤資訊。

你可以點選https://segmentfault.com/a/1190000041563611來檢視實現樣例。

重置驗證規則

FromControl提供了clearValidators()來清空驗證器,以及setValidators()來設定驗證器,所以我們可以訂閱使用者型別是否發生變化,在發生變化時,根據情況清空交叉欄位的驗證器,然後再重新對其驗證器進行設定。

優點:

  1. 為動態地新增非同步驗證器提供了一種新的思路

缺點:

  1. 驗證規則不直觀。
  2. 程式碼量大。

重置FromGroup項

FromGroup提供的removeControl()使得我們可以移除其中的FormControl,利用該機制我們可以訂閱使用者型別發生變化後,根據情況來移除、新增相應的FormControl,從而達到動態驗證表單的目的。

示例程式碼 C 層:

export class AppComponent implements OnInit {
  name = 'Angular ' + VERSION.major;
  formGroup = new FormGroup({});
  // 學號
  studentNoFormControl = new FormControl(null, Validators.required);
  // 工號
  teachterNoFormControl = new FormControl(null, Validators.required);
  // 使用者型別
  typeFormControl = new FormControl(null, Validators.required);
  ngOnInit(): void {
    this.formGroup.addControl('name', new FormControl('', Validators.required));
    this.formGroup.addControl('type', this.typeFormControl);

    // 訂閱型別的變化,從而決定在formGroup中新增學號還是工號FormControl
    this.typeFormControl.valueChanges.subscribe((type) => {
      if (type === 0) {
        this.formGroup.removeControl('studentNo');
        this.formGroup.addControl('teacherNo', this.teachterNoFormControl);
      } else {
        this.formGroup.removeControl('teacherNo');
        this.formGroup.addControl('studentNo', this.studentNoFormControl);
      }
    });

    // 初始化使用者型別為教師
    this.typeFormControl.setValue(0);
  }

  onSubmit(): void {
    alert('submit');
  }

  /**
   * 顯示學號或是工號的input
   */
  showStudent(): boolean {
    return this.typeFormControl.value === 1;
  }
}

V 層:

<hello name="{{ name }}"></hello>
<p>表單動態驗證示例</p>
<pre>{{ formGroup.invalid | json }}</pre>
<pre>{{ formGroup.get('type').value | json }}</pre>
<form [formGroup]="formGroup">
  <div>姓名:<input type="text" formControlName="name" /></div>
  <div>
    使用者型別:
    <label
      ><input type="radio" [value]="0" formControlName="type" name="type" />
      教師</label
    >
    <label
      ><input type="radio" [value]="1" formControlName="type" name="type" />
      學生</label
    >
  </div>
  <div *ngIf="showStudent()">
    學號:<input type="text" formControlName="studentNo" />
  </div>
  <div *ngIf="!showStudent()">
    工號:<input type="text" formControlName="teacherNo" />
  </div>
  <button [disabled]="formGroup.invalid" (click)="onSubmit()">Submit</button>
</form>

優點:

  1. 直接在FormControl上設定驗證器,程式碼直觀。
  2. 可以直接使用angular提供的ngvalid等class屬性,快速定義校驗結果的樣式。

缺點:

  1. 驗證器會對獲取FromGroup的值產生影響。比如在後續對FormGroup獲取相關的值的操作中,需要對FormGroup是否有值來進行判斷,容易產生在undefined上呼叫value的錯誤。(這可以使用?來規避 ---- formGroup.get('xxx')?.value

相關文章