如何解決 ExpressionChangedAfterItHasBeenCheckedError

Error: ExpressionChangedAfterItHasBeenCheckedError:

最近改了一些我認為應該不會跳錯的東西
結果今天一看突然出現錯誤,

這個錯誤我只看得懂我元件的 (.html)
於是就去看

image

可是看了之後更疑惑了

因為之前就已經用這種寫法了(可能之前都用錯但是沒跳錯誤?)

和這個欄位相關的有

.html

<mat-card>
  <form [formGroup]="uploadPhotoForm">
    <mat-card class="groupBorder">
      <mat-tab-group
        mat-stretch-tabs
        dynamicHeight
        [(selectedIndex)]="idcardUploadTabIndex"
        (focusChange)="tabFocusChange($event)"
        (selectedTabChange)="tabChanged($event)"
      >
        <mat-tab label="第一">
          <h5>第一</h5>
          <div
            class=""  
          >
            <b>分公司:</b>

            <mat-form-field appearance="standard">
              <mat-select
                formControlName="BrhNo"
                placeholder="開戶分公司"
                [value]="uploadPhotoForm.controls.BrhNo.value"
              >
                <mat-option>--</mat-option>
                <mat-option
                  *ngFor="let companyBranch of companyBranchList"
                  [value]="companyBranch.value"
                >
                  {{ companyBranch.branchName }}
                </mat-option>
              </mat-select>
              <mat-error *ngIf="uploadPhotoForm.get('BrhNo')?.invalid"
                >{{ getErrorMessage('BrhNo') }}
              </mat-error>
            </mat-form-field>
          </div>
        </mat-tab>

        <mat-tab label="第二">
          <h5>第二</h5>
          <div
            class=""       
          >
            <b>分公司:</b>

            <mat-form-field appearance="standard">
              <mat-select
                formControlName="BrhNo"
                placeholder="開戶分公司"
                [value]="uploadPhotoForm.controls.BrhNo.value"
              >
                <mat-option>--</mat-option>
                <mat-option
                  *ngFor="let companyBranch of companyBranchList"
                  [value]="companyBranch.value"
                >
                  {{ companyBranch.branchName }}
                </mat-option>
              </mat-select>
              <mat-error *ngIf="uploadPhotoForm.get('BrhNo')?.invalid"
                >{{ getErrorMessage('BrhNo') }}
              </mat-error>
            </mat-form-field>
          </div>
        </mat-tab>
      </mat-tab-group>
    </mat-card>
  </form>
</mat-card>

.ts


  // 身分證上傳相關
  uploadPhotoForm = this.idcardUploadService.uploadPhotoForm;

 ngOnInit() {
    // 設置欄位必填
    this.setIdCardUploadValidators();

}

  // 設置欄位驗證
  setIdCardUploadValidators() {

    this.uploadPhotoForm.controls.BrhNo.setValidators([Validators.required]);
    this.uploadPhotoForm.controls.BrhNo.markAsTouched();

  }
idcard-upload.service.ts

// 身分證上傳相關
  uploadPhotoForm = this.fb.group({
    BrhNo: new FormControl(),  
  });


環境

然後我自己是懷疑
我有透過偵測 tabIndex 變動
去setValue 到其他欄位
其他欄位再偵測變動改變,
可是好像不是這問題,
但還是附上來

imgsType: Img[] = [
    { id: 1, name: '第一證件', isdisabled: true },
    { id: 2, name: '第二證件', isdisabled: true },
    { id: 9, name: '其他證件', isdisabled: true },
  ];

  rolecodes: RoleCode[] = [
    { id: 0, name: '委託人', isdisabled: false },
    { id: 1, name: '法定代理人', isdisabled: false },
    { id: 2, name: '相關人', isdisabled: false },
  ];

 ngOnInit() {
   
    this.IDKindSubscribe =
      this.uploadPhotoForm.controls.IDKind.valueChanges.subscribe(res => {
        // 證件種類配合tab連動證件種類選項disabled
        this.imgTypeOptionDisabledControl();

      });
  }
  imgTypeOptionDisabledControl() {
    const IDKindValue = this.uploadPhotoForm.controls.IDKind.value;
    // console.log('IDKindValue', IDKindValue);

    // 因為配合tabindex連動,讓使用者不要誤點其他的
    switch (IDKindValue) {
      case '1':
        this.imgsType[0].isdisabled = false;
        this.imgsType[1].isdisabled = true;
        this.imgsType[2].isdisabled = true;

        break;

      case '2':
        this.imgsType[0].isdisabled = true;
        this.imgsType[1].isdisabled = false;
        this.imgsType[2].isdisabled = true;

        break;

      case '9':
        this.imgsType[0].isdisabled = true;
        this.imgsType[1].isdisabled = true;
        this.imgsType[2].isdisabled = false;

        break;

      default:
        break;
    }
  }
 // 讓tab變動連動idKind
  tabChanged($event: MatTabChangeEvent) {
    // console.log('tabChanged idcardUploadTabIndex', this.idcardUploadTabIndex);

    switch (this.idcardUploadTabIndex) {
      case 0:
        this.uploadPhotoForm.controls.IDKind.setValue('1');
        break;

      case 1:
        this.uploadPhotoForm.controls.IDKind.setValue('2');
        break;

      case 2:
        this.uploadPhotoForm.controls.IDKind.setValue('9');
        break;

      default:
        break;
    }
  }
  
                        <b>證件種類:</b>
                        <mat-form-field appearance="standard">
                          <mat-label>--選擇種類--</mat-label>
                          <mat-select formControlName="IDKind">
                            <mat-option>--選擇種類--</mat-option>
                            <mat-option
                              *ngFor="let img of imgsType"
                              value="{{ img.id }}"
                              disabled="{{ img.isdisabled }}"
                            >
                              {{ img.name }}
                            </mat-option>
                          </mat-select>
                        </mat-form-field>


抱歉麻煩了

我還有把生命週期的東西引入
把東西丟進去,但還是有錯誤…

抱歉麻煩各位高手了 Q口Q

簡單說,就是在 mat-error 的 ngIf 條件上,在判斷該 control 得資料是否有被異動過 (pristine)

然後設定 FormControl 的 valiator 後,不用在刻意去設定為 touched 吧,不知道這邊的目的是什麼

這塊是因為使用者需求一開始就要跳驗證提醒(通常為必填),所以我設定touched

我加了
.updateValueAndValidity();

就沒跳錯了
這樣是正確解法?
以及為什麼這樣就可以了?

this.uploadPhotoForm.controls.BrhNo.updateValueAndValidity();

使用 updateValueAndValidity 會是正確的用法
可以去看什麼是 ExpressionchangedAfterItHasBeenCheckedError 的原因是什麼
https://angular.io/errors/NG0100

1個讚