组件渲染正常却报TypeError: Cannot read properties of undefined (reading 'company')
问题:Angular组件渲染数据正常但控制台报错
Cannot read properties of undefined (reading 'company') 我尝试通过以下方式从API获取公司数据到Angular组件:
组件代码:
@Component({ selector: 'app-company-details', templateUrl: './company-details.component.html', styleUrls: ['./company-details.component.css'] }) export class CompanyDetailsComponent implements OnInit, OnChanges, OnDestroy { @Input() companyId!: number company! : CompanyInterface subscription! : Subscription constructor(private companiesService : CompaniesService) { } ngOnChanges(changes: SimpleChanges) { } ngOnInit(): void { this.subscription = this.companiesService.getCompanyDetails(this.companyId) .subscribe(response => { this.company = response }) } ngOnDestroy() { this.subscription.unsubscribe() } }
模板代码:
<article class="company-details"> <h4>COMPANY DETAILS</h4> <div class="company-description"> <h5>DESCRIPTION</h5> <p>{{company.description}}</p> </div> <div class="company-locations"> <h5>LOCATIONS</h5> <p>We are in: <span *ngFor="let location of company.locations">{{location.name}},</span> </p> </div> <div class="company-size"> <span><h5>SIZE: {{company.size.short_name | uppercase}}</h5> </span> </div> <mat-divider></mat-divider> </article>
组件能正常渲染数据,但控制台始终报错:ERROR TypeError: Cannot read properties of undefined (reading 'company'),请问哪里操作有误?这是否和组件生命周期相关?
原因与解决方案
核心原因
这个错误完全和组件生命周期、数据加载时机有关:
- 初始渲染时
company未定义:组件初始化后立即渲染模板,但ngOnInit里的API请求是异步的,返回数据前company还是undefined,模板直接访问company.description、company.locations等属性就会报错。 @Input() companyId的更新未处理:如果父组件后续更新companyId,当前代码的ngOnChanges是空实现,不会重新发起API请求,新的companyId不会触发数据更新,同时如果此时company状态异常,也会导致报错。
解决方案
方案1:使用安全导航操作符+条件渲染
在模板中给所有company属性访问加上?.,同时用*ngIf控制模板渲染时机,避免访问未定义对象的属性:
<article class="company-details"> <h4>COMPANY DETAILS</h4> <div class="company-description" *ngIf="company"> <h5>DESCRIPTION</h5> <p>{{company?.description}}</p> </div> <div class="company-locations" *ngIf="company"> <h5>LOCATIONS</h5> <p>We are in: <span *ngFor="let location of company?.locations">{{location.name}},</span> </p> </div> <div class="company-size" *ngIf="company"> <span><h5>SIZE: {{company?.size?.short_name | uppercase}}</h5> </span> </div> <mat-divider></mat-divider> </article>
方案2:初始化company为默认空对象
在组件类中给company设置符合CompanyInterface结构的默认值,避免初始状态为undefined:
company: CompanyInterface = { description: '', locations: [], size: { short_name: '' } // 其他接口属性需按结构补充初始化 };
方案3:完善ngOnChanges处理companyId更新
如果父组件可能动态修改companyId,需要在ngOnChanges中监听变化,取消旧订阅并重新请求数据:
ngOnChanges(changes: SimpleChanges) { if (changes['companyId'] && !changes['companyId'].firstChange) { this.subscription?.unsubscribe(); this.fetchCompanyDetails(); } } ngOnInit(): void { this.fetchCompanyDetails(); } private fetchCompanyDetails() { this.subscription = this.companiesService.getCompanyDetails(this.companyId) .subscribe(response => { this.company = response; }); }
方案4:使用async管道(推荐)
用async管道自动管理订阅与取消订阅,同时避免手动处理异步数据的未定义问题:
- 组件类定义可观察对象:
@Input() companyId!: number; company$!: Observable<CompanyInterface>; ngOnChanges(changes: SimpleChanges) { if (changes['companyId']) { this.company$ = this.companiesService.getCompanyDetails(this.companyId); } } ngOnInit(): void { this.company$ = this.companiesService.getCompanyDetails(this.companyId); }
- 模板中使用
async管道:
<article class="company-details" *ngIf="company$ | async as company"> <h4>COMPANY DETAILS</h4> <div class="company-description"> <h5>DESCRIPTION</h5> <p>{{company.description}}</p> </div> <div class="company-locations"> <h5>LOCATIONS</h5> <p>We are in: <span *ngFor="let location of company.locations">{{location.name}},</span> </p> </div> <div class="company-size"> <span><h5>SIZE: {{company.size.short_name | uppercase}}</h5> </span> </div> <mat-divider></mat-divider> </article>
async管道会自动处理订阅生命周期,且*ngIf="company$ | async as company"会在数据返回后才渲染模板,彻底避免未定义报错,还能简化组件代码。
内容的提问来源于stack exchange,提问作者Armando_pagano
相关产品推荐
相关产品推荐

