Angular读取nativeElement出现undefined报错排查
问题现象
当页面已经存在添加完成的字段时,功能可正常运行;但字段为空、首次尝试拉取新字段时,接口返回成功提示后抛出Cannot read properties of undefined (reading 'nativeElement')错误,且该错误不会进入HTTP请求的error回调分支。因项目整体代码体量较大,仅贴出相关核心代码片段,排查思路整理如下。
相关核心代码
模板代码
<div> <button (click)="checkTableFields()"> Get Service Fields </button> </div>
组件定义与拉取入口逻辑
public fieldServiceFormGroup: FormGroup; public camposServicos: any[]; @ViewChild("fieldsService", { static: false }) ngForm: NgForm; @ViewChildren("labelFocus") labelFocus: QueryList<any>; Mensagens = MensagemInformativaService; checkTableFields() { const control = this.campoServicoFormGroup.get("items") as FormArray; if (control.length < 1) { this.searchTableFields(true); } else { if (control.length > 0) { const options = { title: "Confirmation", message: `There are already fields filled in. Do you want to fetch more fields from the service table?`, textCancel: "CANCEL", ConfirmationText: "CONFIRM", }; this.confirmationDialogService.abrir(options); this.confirmationDialogService.confirmation().subscribe((response) => { if (response) { this.searchTableFields(true); } }); } } }
表单项创建方法
createItem() { return this.fb.group({ code: "", service: this.serviceSelected, label: ["", Validators.required], tablefield: ["", Validators.required], maskField: ["", Validators.required], active: [true, Validators.required], ordering: "", }); }
接口拉取字段逻辑
findTableFields(scroll: boolean) { this.servicesApiService .getFieldsTable(this._serviceSelected.cod) .subscribe( (data: Array<any>) => { const control = this.fieldServicoFormGroup.get("items") as FormArray; data.forEach((field) => { control.push(this.createItemFilled(field)); to control .at(control.length - 1) .get("MascaraField") .setValue(this.masksFields[0]); if (roll) { setTimeout(() => { this.document.getElementById("scrollframe").scrollIntoView({ behavior: "smooth", block: "center", inline: "closest", }); }, 500); } }); this.changeDetectorRef.detectChanges(); setTimeout(() => { this.labelFocus .toArray()[this.fieldsServices.length].nativeElement.focus(); }, 200); this.servicoMensagem.setMensagemTexto( "Fields added successfully. You must save changes for the fields to be applied to the service." ); }, (error) => { this.servicoMensagem.setMensagemTexto(error); } ); }
报错信息
core.js:6014 ERROR TypeError: Cannot read properties of undefined (reading 'nativeElement') at tab-fields-service.component.ts:292:43 at ZoneDelegate.invokeTask (zone-evergreen.js:391:1) at Object.onInvokeTask (core.js:39680:1) at ZoneDelegate.invokeTask (zone-evergreen.js:390:1) at Zone.runTask (zone-evergreen.js:168:1) at invokeTask (zone-evergreen.js:465:1) at ZoneTask.invoke (zone-evergreen.js:454:1) at timer (zone-evergreen.js:2650:1)
排查与修复思路
- 错误触发逻辑定位:报错出现在
setTimeout异步回调中,属于HTTP请求成功回调后的异步执行逻辑,因此不会进入HTTP请求的error回调分支。报错本质是this.labelFocus.toArray()[索引]取值为undefined,访问其nativeElement属性时触发类型错误。 - 索引计算错误问题:
代码中用于获取DOM元素的索引值为this.fieldsServices.length,该值与@ViewChildren("labelFocus")实际采集到的DOM元素数量不匹配:- 首次拉取字段时,原表单为空,若
this.fieldsServices未同步更新新增字段数据,计算出的索引会超出QueryList实际元素长度,直接取到undefined - 现有代码存在大量拼写不一致问题:点击事件中调用的方法为
searchTableFields,实际定义的拉取方法为findTableFields;表单组变量名存在fieldServiceFormGroup、campoServicoFormGroup、fieldServicoFormGroup三种写法;滚动参数形参为scroll,判断时写为roll;表单字段定义名为maskField,赋值时取的是MascaraField。这类拼写错误极易导致取值与预期不符,直接造成索引越界。
- 首次拉取字段时,原表单为空,若
- DOM渲染时序不可靠问题:
虽然手动调用了changeDetectorRef.detectChanges(),但写死200ms延时再获取DOM的方式稳定性极差,尤其是首次加载时页面DOM结构未完成渲染,QueryList未采集到所有新增的带#labelFocus标记的元素,此时按索引取元素必然会拿到undefined。 - 修复方案:
- 统一所有变量、方法、表单字段名的拼写,消除基础引用错误
- 移除手动计算索引的逻辑,
QueryList原生提供last属性可直接获取最后一个元素,无需手动计算长度取索引,从根源避免索引越界 - 移除固定延时取DOM的逻辑,改为订阅
this.labelFocus.changes,等QueryList完成元素采集、更新完毕后再执行focus操作,彻底解决时序问题 - 将滚动逻辑从
forEach循环中移出,现有逻辑每添加一个字段就触发一次滚动没有实际意义,等所有字段添加完成、DOM渲染完毕后执行一次滚动即可。
内容的提问来源于stack exchange,提问作者João
相关产品推荐
相关产品推荐

