Angular通过Modal Service传入自定义组件并渲染到弹窗的实现问题
实现方案
你之前尝试container ref方案未成功,主要是因为传入的是调用方视图的容器引用,而我们需要将自定义组件渲染到弹窗内部的占位容器中,直接在弹窗组件内部获取对应容器引用即可,不需要外部传入。完整实现步骤如下:
第一步:修改ModalService,支持传入自定义组件
import { Injectable, Type } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ModalService { private display = new BehaviorSubject<'open' | 'close'>('close'); // 存储要渲染的自定义组件 private modalContent = new BehaviorSubject<Type<any> | null>(null); watch(): Observable<'open' | 'close'> { return this.display.asObservable(); } // 监听要渲染的内容 watchContent(): Observable<Type<any> | null> { return this.modalContent.asObservable(); } // 改造open方法,接受自定义组件作为入参 open(component: Type<any>): void { this.modalContent.next(component); this.display.next('open'); } close(): void { this.display.next('close'); // 关闭后延迟清空内容,适配弹窗关闭动画时长 setTimeout(() => this.modalContent.next(null), 300); } }
第二步:修改CustomModalComponent模板,添加内容占位容器
<ng-container *ngIf="display$ | async as display"> <aside [ngClass]="display === 'open' ? '' : 'hidden'" class="fixed inset-0 z-50 flex" role="dialog" aria-modal="true"> <!-- 自定义内容占位符 --> <ng-template #modalContent></ng-template> </aside> </ng-container>
第三步:修改CustomModalComponent逻辑,动态渲染组件
import { Component, OnInit, ViewChild, ViewContainerRef, Type, OnDestroy, ComponentRef } from '@angular/core'; import { Observable, Subscription } from 'rxjs'; import { ModalService } from './modal.service'; @Component({ selector: 'app-custom-modal', templateUrl: './custom-modal.component.html' }) export class CustomModalComponent implements OnInit, OnDestroy { display$: Observable<'open' | 'close'>; // 获取占位容器的引用 @ViewChild('modalContent', { read: ViewContainerRef, static: true }) modalContentRef!: ViewContainerRef; private contentSub?: Subscription; private currentComponent?: ComponentRef<any>; constructor(private modalService: ModalService) { } ngOnInit(): void { this.display$ = this.modalService.watch(); // 监听内容变化,动态创建组件 this.contentSub = this.modalService.watchContent().subscribe(component => { // 先清空之前的内容,避免重复渲染 this.modalContentRef.clear(); this.currentComponent?.destroy(); if (component) { // 动态创建组件并插入到占位容器 this.currentComponent = this.modalContentRef.createComponent(component); // 如需给自定义组件传输入参数,可直接操作instance:this.currentComponent.instance.参数名 = 对应值 // 如需监听自定义组件的输出事件,可直接订阅:this.currentComponent.instance.输出事件.subscribe(回调) } }); } close(): void { this.modalService.close(); } ngOnDestroy(): void { // 销毁订阅和组件,避免内存泄漏 this.contentSub?.unsubscribe(); this.currentComponent?.destroy(); } }
使用示例
调用open方法时直接传入要渲染的组件即可:
import { YourCustomComponent } from './your-custom.component'; constructor(private modalService: ModalService) {} openCustomModal() { // 直接传入要展示的组件类型 this.modalService.open(YourCustomComponent); }
补充说明
- 如果你需要支持传入模板作为内容,可扩展
modalContent的类型支持TemplateRef,判断类型为模板时调用createEmbeddedView渲染即可 - Angular 9+不需要将动态渲染的组件加入
entryComponents数组,只要组件在对应NgModule的declarations中声明,或是独立组件即可正常使用
内容的提问来源于stack exchange,提问作者da1lbi3
相关产品推荐
相关产品推荐

