ExpressionChangedAfterItHasBeenCheckedError报错--项目中遇到的问题

本文介绍了一个在Angular应用中常见的问题——ExpressionChangedAfterItHasBeenCheckedError错误。该错误通常出现在使用异步方式更新数据并试图同步视图的情况下。文章通过一个级联选择下拉框的例子展示了如何手动触发变更检测来避免此问题。

关于 ExpressionChangedAfterItHasBeenCheckedError 错误你所需要知道的事情
参考文章 https://segmentfault.com/a/1190000013972657

下面介绍一下今天遇到的这个报错

场景:

有多个级联选择下拉框,所有的数据都是一次性获取到的,在编辑页面需要将这些值回显到页面上,此时不使用异步方式处理会造成值无法渲染

/************html 文件***************/
<!-- 城市 -->
<nz-select *ngSwitchCase="'provinceId'" [(ngModel)]="area.provinceId"
  formControlName="{{level1.englishName}}" style="width: 240px;" nzAllowClear
  nzPlaceHolder="请选择{{level1.name}}" (ngModelChange)="changeProvince()">
  <nz-option *ngFor="let option of country?.provinces[area.countryId]"
    nzValue="{{option.cityId}}" nzLabel="{{option.cityName}}">
  </nz-option>
</nz-select>

<!-- 区域 -->
<nz-select *ngSwitchCase="'cityId'" formControlName="{{level1.englishName}}"
  style="width: 240px;" [(ngModel)]="area.cityId" nzAllowClear
  nzPlaceHolder="请选择{{level1.name}}">
  <nz-option *ngFor="let option of country?.citys[area.provinceId]"
    nzValue="{{option.cityId}}" nzLabel="{{option.cityName}}"></nz-option>
</nz-select>

/******ts 文件******/
export class Area {
  countryId = 1;
  provinceId: any;
  cityId: any;
}

switch (elem.englishName) {
   case 'provinceId':
     this.area.provinceId = elem.value;
     break;
   case 'cityId':
     setTimeout(() => { //  =====================异步方式渲染
       this.area.cityId = elem.value;
     })
     break;
}
解决

但是如果当有很多个异步方式渲染时便会出现标题上所示的错误,造成改错误的原因是angular变更检测后重新更改值可能造成视图和model中的值不统一,所以我们需要手动触发检测

import {ChangeDetectorRef } from '@angular/core';

constructor( private cd: ChangeDetectorRef ) { }

case 'sceneId':
	setTimeout(() => {
	  this.mediaModel.scenesId = elem.value;
	})
	break;
case 'formId':
	setTimeout(() => {
	  this.mediaModel.formId = elem.value;
	  this.cd.detectChanges(); // ================变更检测
	})
	break;
case 'pointId':
	setTimeout(() => {
	  this.mediaModel.pointId = elem.value;
	  this.cd.detectChanges();
	})
	break;
public sidePaneForm: FormGroup = this.formBuilder.group({ displayName: new FormControl('', [ Validators.required, Validators.minLength(dataSourceNameMinimumLength), Validators.maxLength(dataSourceNameMaximumLength), validateSourceAndDestinationName, ]), connection: new FormControl('', Validators.required), method: new FormControl([''], Validators.required), requestHeaders: new FormControl([]), requestParameters: new FormControl([]), requestBody: new FormControl('', jsonValidator()), pollIntervalMs: new FormControl(15000, [Validators.required, Validators.min(1000), Validators.max(360000)]), maxRetries: new FormControl(10, [Validators.required, Validators.min(10), Validators.max(100)]), retryBackoffMs: new FormControl(3000, [Validators.required, Validators.min(1000)]), retriableHttpStatusCodes: new FormControl('400', [Validators.required, Validators.maxLength(100)]), }); public resetFormItemValue(): void { this.resetFormValue(['displayName', 'connection', 'requestBody']); this.sidePaneForm.controls.method.setValue(['']); this.sidePaneForm.controls.requestParameters.setValue([]); this.sidePaneForm.controls.requestHeaders.setValue([]); this.sidePaneForm.controls.pollIntervalMs.setValue(15000); this.isShowEditor = false; this.requestHeadersArr = []; this.requestParametersArr = []; this.connectionError = ''; this.connectionConfig = { ...this.connectionConfig, optionList: [], placeholder: this.TridentStreaming_SelectCloudConnection, chooseOption: null, }; } public resetFormValue(resetControlNameList?: string[]) { this.facade?.updateRightPaneSaveButtonDisable(true); const keys = Object.keys(this.sidePaneForm.controls); resetControlNameList.forEach(name => { if (keys.some(item => item === name)) { this.sidePaneForm.controls[name]?.setValue(''); } }); this.sidePaneForm.markAsPristine(); } 报错如下 eventstream-error-handler.ts:15 RuntimeError: NG01002: Must supply a value for form control at index: 0 at eval (abstract_model.ts:155:13) at eval (form_array.ts:473:7) at Array.forEach (<anonymous>) at FormArray._forEachChild (form_array.ts:472:19) at assertAllValuesPresent (abstract_model.ts:153:11) at FormArray.setValue (form_array.ts:284:5) at HttpSidePaneComponent.resetFormItemValue (http-side-pane.component.ts:514:54) at HttpSidePaneComponent.init (http-side-pane.component.ts:595:14) at set instance (http-side-pane.component.ts:399:14) at setInputsForProperty (core.mjs:11646:34) 但是改为下面的代码就没问题了,为什么 public resetFormItemValue(): void { this.sidePaneForm.controls.displayName.setValue(''); this.sidePaneForm.controls.connection.setValue(''); this.sidePaneForm.controls.requestBody.setValue(''); this.sidePaneForm.controls.method.setValue(['']); this.sidePaneForm.controls.requestParameters.setValue([]); this.sidePaneForm.controls.requestHeaders.setValue([]); this.sidePaneForm.controls.pollIntervalMs.setValue(15000); this.isShowEditor = false; this.requestHeadersArr = []; this.requestParametersArr = []; this.connectionError = ''; this.connectionConfig = { ...this.connectionConfig, optionList: [], placeholder: this.TridentStreaming_SelectCloudConnection, chooseOption: null, }; }
最新发布
09-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值