Angular依赖注入报错:Cannot read properties of undefined
你遇到这个报错的核心原因是错误地通过依赖注入来获取子组件实例,而且组件的装饰器使用也不符合Angular的规则,咱们一步步来修正问题:
问题1:子组件同时使用了@Component和@Injectable({providedIn: 'root'})
Angular的组件(带@Component的类)是用来渲染视图的,需要通过模板标签实例化,不能像服务那样用@Injectable提供成全局实例。你给ChildComponent加了@Injectable,会导致DI系统创建一个独立的、没有关联视图的组件实例,这个实例里的element和observable$都是未定义的,这就是你报错的根源。
修正:删掉ChildComponent上的@Injectable装饰器
问题2:父组件通过构造函数注入子组件
你在ParentComponent的构造函数里注入ChildComponent,这种方式拿到的不是模板中渲染的子组件实例,而是DI系统创建的空实例(或者直接报错)。正确的做法是在父组件模板中声明子组件,然后用@ViewChild获取它的实例。
修正步骤:
- 给
ChildComponent添加一个selector(比如selector: 'app-child'),这样父组件模板可以引用它 - 在
ParentComponent的模板中添加<app-child></app-child>标签 - 在
ParentComponent中用@ViewChild(ChildComponent)获取子组件实例
问题3:AppModule里的错误依赖配置
你在providers里配置了{provide: ParentComponent, deps: [ChildComponent]},这完全没必要,Angular组件的实例是由模板渲染系统创建的,不需要手动在providers里声明依赖。
修正:删掉这个providers配置项
修正后的完整代码:
child.component.ts
import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; import { fromEvent, Observable } from 'rxjs'; @Component({ selector: 'app-child', // 新增selector,供父组件模板使用 template: '<div #myElement>点击我测试</div>' // 新增模板,包含要监听的元素 }) export class ChildComponent implements AfterViewInit { @ViewChild("myElement") element: ElementRef<Element>; observable$: Observable<any>; constructor() { } ngAfterViewInit(): void { this.observable$ = fromEvent(this.element.nativeElement, "click"); } }
parent.component.ts
import { Component, AfterViewInit, ViewChild } from '@angular/core'; import { ChildComponent } from './child.component'; @Component({ template: '<app-child></app-child>' // 渲染子组件 }) export class ParentComponent implements AfterViewInit { // 用@ViewChild获取模板中的子组件实例 @ViewChild(ChildComponent) childComponent!: ChildComponent; constructor() { } ngAfterViewInit() { // 现在可以安全访问子组件的observable$了 this.childComponent.observable$.subscribe(() => { console.log('子组件元素被点击了'); }); } }
app.module.ts
import { NgModule } from '@angular/core'; import { ParentComponent } from './parent.component'; import { ChildComponent } from './child.component'; @NgModule({ declarations: [ ParentComponent, ChildComponent ], // 删掉错误的providers配置 providers: [] }) export class AppModule { }
这样修改后,父组件就能正确获取到子组件的实例,并且在ngAfterViewInit时,子组件的observable$已经完成初始化,就不会再出现“Cannot read properties of undefined”的报错了。
备注:内容来源于stack exchange,提问作者John Lester

