Angular动态组件传参报错:Unhandled Promise rejection: No provider for (data)
解决Angular动态组件数据注入时的"No provider"错误
你遇到的这个No provider for (data)错误,本质是因为你尝试通过Injector.get('new_data')获取数据时,这个new_data的标识(token)并没有在Angular的注入器中注册对应的提供者,Angular找不到它的来源,自然就抛出了找不到提供者的异常。下面给你两种解决思路,其中第一种是更符合Angular规范的做法:
方法一:用@Input()传递数据(推荐)
其实Angular动态组件的输入数据完全不需要通过Injector来获取,直接用组件的输入属性就能搞定,这是官方推荐的组件间数据传递方式:
- 修改
NewsFeedComponent,定义输入属性
在你的新闻组件里,直接用@Input()装饰器声明要接收的属性:
import { Component, Input } from '@angular/core'; @Component({ selector: 'app-news-feed', template: `<!-- 这里是你的组件模板 -->` }) export class NewsFeedComponent { @Input() new_data: any; // 声明输入属性 // 之后直接用this.new_data就能拿到传递过来的数据了 }
- 调整动态组件加载逻辑,为实例赋值输入属性
假设你的<dynamic-component>是用来加载动态组件的容器组件,需要在它里面处理组件实例的输入属性赋值:
import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core'; @Component({ selector: 'dynamic-component', template: `<ng-template #dynamicContainer></ng-template>` }) export class DynamicComponent { @Input() set componentData(data: any) { if (data) { this.loadDynamicComponent(data); } } @ViewChild('dynamicContainer', { read: ViewContainerRef }) private dynamicContainer: ViewContainerRef; constructor(private resolver: ComponentFactoryResolver) {} private loadDynamicComponent(componentConfig: { component: any, inputs: any }) { // 先清空容器里的旧组件 this.dynamicContainer.clear(); // 创建组件工厂 const componentFactory = this.resolver.resolveComponentFactory(componentConfig.component); // 创建组件实例 const componentRef = this.dynamicContainer.createComponent(componentFactory); // 把输入数据赋值给组件实例 Object.assign(componentRef.instance, componentConfig.inputs); // 手动触发变更检测(如果组件的变更检测策略是OnPush的话) componentRef.changeDetectorRef.detectChanges(); } }
这样你原来的调用代码完全不用改,NewsFeedComponent也不需要依赖Injector,直接用this.new_data就能拿到接口返回的数据了,简单又规范。
方法二:通过自定义注入器传递数据
如果你因为特殊场景一定要用Injector来获取数据,那需要在创建动态组件时,手动创建一个包含自定义提供者的子注入器:
- 修改动态组件的加载逻辑,创建子注入器
在<dynamic-component>的加载方法里,基于当前注入器创建一个包含new_data提供者的子注入器:
import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, Injector } from '@angular/core'; @Component({ selector: 'dynamic-component', template: `<ng-template #dynamicContainer></ng-template>` }) export class DynamicComponent { @Input() set componentData(data: any) { if (data) { this.loadDynamicComponent(data); } } @ViewChild('dynamicContainer', { read: ViewContainerRef }) private dynamicContainer: ViewContainerRef; constructor( private resolver: ComponentFactoryResolver, private injector: Injector // 注入当前组件的注入器 ) {} private loadDynamicComponent(componentConfig: { component: any, inputs: any }) { this.dynamicContainer.clear(); const componentFactory = this.resolver.resolveComponentFactory(componentConfig.component); // 创建包含自定义提供者的子注入器 const childInjector = Injector.create({ providers: [ { provide: 'new_data', useValue: componentConfig.inputs.new_data } ], parent: this.injector // 继承父注入器的所有提供者 }); // 使用子注入器创建组件 const componentRef = this.dynamicContainer.createComponent(componentFactory, undefined, childInjector); } }
NewsFeedComponent中通过Injector获取数据
这时候你原来的组件代码就可以正常工作了:
import { Component, Injector } from '@angular/core'; @Component({ selector: 'app-news-feed', template: `<!-- 你的组件模板 -->` }) export class NewsFeedComponent { data: any; constructor(private injector: Injector) { this.data = this.injector.get('new_data'); } }
不过这种方式相对复杂,也不符合Angular的常规数据传递思路,除非有特殊需求,否则还是推荐第一种方法。
内容的提问来源于stack exchange,提问作者Yazan Mehrez
相关产品推荐
相关产品推荐

