Angular:如何通过Injector在ngComponentOutlet中传值及解决组件创建报错
我尝试在Angular中动态创建并展示组件,需要向其传递数据来展示内容,但遇到了报错。以下是我的代码:
HTML代码
<div class="basicContainer"> <div class="projectsTreeContainer"> <input type="text" id="searchWord" placeholder="Search through projects"/> <button (click)="loadAddProject()">Add new Project</button> <app-projects-tree (onLoadProjectDetails)="loadProjectDetails($event)" (onLoadWpDetails)="loadWpDetails($event)" (onSelectAndLoadJobDetails)="loadJobDetails($event)"></app-projects-tree> </div> <div class="infoContainer"> <ng-container *ngComponentOutlet="details"></ng-container> </div> </div>
父组件代码
export class ProjectsComponent implements OnInit { details: Component; private showWp: boolean; constructor() { } loadProjectDetails(project: BasicProject): void { this.details = new ProjectComponent(project); } }
目标动态组件代码
export class ProjectComponent implements OnInit { project: Project; constructor(basicProject: BasicProject) { this.project = new Project(); this.project.name = basicProject.name ; } }
NgModule配置
@NgModule({ declarations: [ AppComponent, NavbarComponent, LoginComponent, ProjectsComponent, ProjectComponent, ProjectsTreeComponent, TreeProjectComponent, TreeWpComponent, WpComponent, JobComponent, AddProjectComponent ], imports: [ BrowserModule, FormsModule, RouterModule.forRoot( AppRoutes.appRoutes, // {enableTracing: true} // <-- debugging purposes only ), HttpClientModule, HttpModule ], providers: [ AuthenticationService, ProjectsService, CanActivateAuthGuard, { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }], bootstrap: [AppComponent], entryComponents: [ProjectComponent, WpComponent, JobComponent, AddProjectComponent] })
报错信息
ProjectsComponent.html:15 ERROR Error: No component factory found for [object Object].
请问该如何解决这个问题,完成动态创建组件并传递数据的需求?
解决方案
你遇到的核心问题是直接实例化组件类(new ProjectComponent(...))不符合Angular的组件创建机制,Angular需要通过组件工厂来创建组件实例,而且*ngComponentOutlet接受的是组件类型(比如ProjectComponent),而不是已经实例化的对象。下面是具体的修复步骤:
1. 修正父组件中组件类型的定义与创建逻辑
首先,details应该存储组件的类型,而不是实例。同时注入ComponentFactoryResolver来处理组件创建:
import { Component, OnInit, ComponentFactoryResolver, Injector } from '@angular/core'; import { ProjectComponent } from './project.component'; import { BasicProject } from './models/basic-project.model'; export class ProjectsComponent implements OnInit { // 存储要展示的组件类型 detailsComponentType: any; // 存储当前要传递的项目数据 currentProject: BasicProject; constructor( private componentFactoryResolver: ComponentFactoryResolver, private injector: Injector ) { } loadProjectDetails(project: BasicProject): void { this.currentProject = project; // 赋值组件类型,而非实例 this.detailsComponentType = ProjectComponent; } // 创建自定义注入器,用于传递数据 createProjectInjector(): Injector { return Injector.create({ parent: this.injector, providers: [{ provide: BasicProject, useValue: this.currentProject }] }); } }
2. 修改HTML模板,通过*ngComponentOutlet传递数据
*ngComponentOutlet支持通过自定义注入器传递依赖,调整模板如下:
<div class="infoContainer"> <ng-container *ngComponentOutlet="detailsComponentType; injector: createProjectInjector()"></ng-container> </div>
3. 调整目标组件的构造函数,通过依赖注入获取数据
目标组件不能直接在构造函数中接收普通参数,需要通过Angular的依赖注入机制获取数据:
import { Component, OnInit, Inject } from '@angular/core'; import { BasicProject } from './models/basic-project.model'; import { Project } from './models/project.model'; export class ProjectComponent implements OnInit { project: Project; constructor(@Inject(BasicProject) private basicProject: BasicProject) { this.project = new Project(); this.project.name = basicProject.name; } ngOnInit(): void { // 可在此添加更多初始化逻辑 } }
4. 确认entryComponents配置(Angular 9+可省略)
你的NgModule中已经将ProjectComponent加入entryComponents,这在Angular 9之前是动态组件必须的配置(用于提前编译)。Angular 9及以后启用Ivy编译器后,该配置可省略,但保留也不会有问题。
备选方案:手动创建组件实例(更精细控制)
如果需要对组件实例做更多自定义操作,也可以手动通过ComponentFactoryResolver创建组件:
首先在父组件模板中添加容器引用:
<div class="infoContainer"> <div #dynamicComponentContainer></div> </div>
然后修改父组件代码:
import { Component, OnInit, ComponentFactoryResolver, ViewChild, ViewContainerRef } from '@angular/core'; export class ProjectsComponent implements OnInit { @ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) container: ViewContainerRef; constructor(private componentFactoryResolver: ComponentFactoryResolver) { } loadProjectDetails(project: BasicProject): void { // 清空容器内已有组件 this.container.clear(); // 获取组件工厂 const factory = this.componentFactoryResolver.resolveComponentFactory(ProjectComponent); // 创建组件实例 const componentRef = this.container.createComponent(factory); // 直接给组件实例赋值数据(无需依赖注入) componentRef.instance.project = new Project({ name: project.name }); } }
这样修改后,就能正确实现动态创建组件并传递数据的需求了。核心是要遵循Angular的组件生命周期和依赖注入机制,不要直接实例化组件类。
内容的提问来源于stack exchange,提问作者Unknwn Artist

