Angular ngFor渲染WebAPI动态表单出现NG0100报错问题
问题场景
通过WebAPI加载问题列表,根据字段questionType区分题目类型,对应答案渲染为单选选项/文本输入框两种形式。需要对用户填写的答案实现必填校验/自定义校验,因此在WebAPI返回问题列表数据后,动态向FormGroup中添加对应的FormControl。
异常表现
- 首次运行报错:
NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '[object Object]'. Current value: 'undefined' - 如果移除HTML模板中的
*ngIf="questionForm"判断,会触发新报错:formGroup expects a FormGroup instance. Please pass one in.
初步判断FormGroup实例已经成功创建,但在Angular生命周期执行的某个节点被重新赋值为undefined。
相关代码
HTML模板代码
<form *ngIf="questionForm" [formGroup]="questionForm"> <ng-container *ngFor="let item of Questions;let i = index;"> <mat-label >{{item.question}}</mat-label> <div [ngSwitch]="item.questionType"> <div *ngSwitchCase="'bool'"> <mat-radio-group class="radio__button" aria-label="Select an option"> <mat-radio-button id="YesAnswer_{{item.id}}" #Answer_{{item.id}} name="Answer_{{item.id}}" value="1" formControlName="Answer_{{item.id}}">Yes</mat-radio-button> <mat-radio-button id="NoAnswer_{{item.id}}" #Answer_{{item.id}} name="Answer_{{item.id}}" value="0" formControlName="Answer_{{item.id}}">No</mat-radio-button> </mat-radio-group> </div> <div *ngSwitchCase="'string'"> <mat-form-field appearance="outline"> <input matInput type="text" id="Answer_{{item.id}}" #Answer_{{item.id}} formControlName="Answer_{{item.id}}" name="Answer_{{item.id}}"> </mat-form-field> </div> </div> </ng-container> </form>
TS文件ngOnInit逻辑
this.Questions存储从WebAPI获取的问题列表,已确认表单初始化逻辑仅在问题列表加载完成后执行,在初始化逻辑后打印this.questionForm可看到FormGroup中已包含所有添加的控件,代码如下:
ngOnInit():void{ this.service.GetQuestion().subscribe({ error: (err) => { console.log(err); }, next: (data) => { if (data) { this.Questions = data as Questions[]; this.questionForm = new FormGroup ({}); this.Questions.forEach(quest=>{ this.questionForm.addControl('Answer_'+String(quest.id),new FormControl( '', [Validators.required])); }); }
问题根因
questionForm没有做初始赋值,组件初始化阶段该属性默认值为undefined,首次模板渲染时如果去掉*ngIf判断,会直接找不到FormGroup实例抛出对应错误。- 异步接口返回的时机晚于Angular首次变更检测执行完成的节点,在订阅回调中重新给
questionForm赋值为新的FormGroup实例,会触发开发模式下的二次变更检测校验,检测到前后值不一致就抛出ExpressionChangedAfterItHasBeenCheckedError,不存在FormGroup被意外赋值为undefined的情况。
修复方案
- 组件类初始化阶段就创建FormGroup实例,不要等异步接口返回后再new,提前给
questionForm赋初始值:// 类属性声明时直接初始化 questionForm: FormGroup = new FormGroup({}); - 异步拿到问题列表数据后,不要重新创建FormGroup,直接往已存在的表单实例上添加控件即可:
ngOnInit():void{ this.service.GetQuestion().subscribe({ error: (err) => { console.log(err); }, next: (data) => { if (data) { this.Questions = data as Questions[]; // 移除重新new FormGroup的逻辑,直接操作已有的实例 this.Questions.forEach(quest=>{ this.questionForm.addControl( 'Answer_'+String(quest.id), new FormControl( '', [Validators.required]) ); }); } } }) } - 如果调整后仍偶发变更检测报错,注入
ChangeDetectorRef,在所有控件添加完成后手动触发一次变更检测即可:import { ChangeDetectorRef } from '@angular/core'; // 构造函数注入 constructor(private cdr: ChangeDetectorRef) {} // 控件添加完成后调用 this.cdr.detectChanges();
内容的提问来源于stack exchange,提问作者PRI
相关产品推荐
相关产品推荐

