Angular 4:Modal组件内动态创建子组件时ViewContainerRef异常
问题分析与解决方案
看起来你遇到的是Angular动态组件渲染时的异步顺序问题——首次点击触发Modal时,渲染动态内容的操作早于Modal内部的standardModalContent容器初始化完成,导致RenderService里的viewContainer还没被赋值,所以出现undefined;第二次点击时,因为相关引用已经存在(或者异步流程刚好赶上),所以能正常工作。
核心问题点
- 在
ModalService.createDialog方法中,你在调用compileModuleAndAllComponentsAsync(异步操作)之后,立刻执行了this.render.renderComponent,但此时StandardModalComponent的ngOnInit还没执行,RenderService.setModalReference还没被调用,所以viewContainer还是undefined。 - 另外,
StandardModalComponent中的@ViewChild('standardModalContent')没有指定{read: ViewContainerRef},默认获取的是ElementRef,这会导致传递给RenderService的不是正确的容器引用。
修复步骤
1. 修正StandardModalComponent的容器引用获取,并添加就绪通知
修改StandardModalComponent,确保正确获取ViewContainerRef,并添加一个Subject来通知外部容器已准备好:
import { Subject } from 'rxjs'; export class StandardModalComponent extends StandardModal implements OnInit { // 明确指定获取ViewContainerRef,而不是默认的ElementRef @ViewChild('standardModalContent', { read: ViewContainerRef }) public standardModalContent: ViewContainerRef; @Input() public appStandardModalHeaderLabel: string; private renderService: RenderService; // 新增Subject,用于通知外部容器已就绪 public contentContainerReady = new Subject<ViewContainerRef>(); constructor(render: RenderService) { super(); this.renderService = render; } ngOnInit() { this.renderService.setModalReference(this.standardModalContent); // 发出容器就绪的通知 this.contentContainerReady.next(this.standardModalContent); this.contentContainerReady.complete(); } }
2. 调整ModalService.createDialog的异步顺序
等待StandardModalComponent的容器就绪后,再调用RenderService.renderComponent:
public createDialog(module: any, component: any, paramsModal?: Object, paramsComponent?: Object) { const componentRef$ = new ReplaySubject(); this.compiler.compileModuleAndAllComponentsAsync(module).then((data) => { const standardModalFactory = data.componentFactories.filter( (singleComponents) => singleComponents.componentType === StandardModalComponent )[0]; const modalRef = this.viewContainerRef.createComponent(standardModalFactory); modalRef.instance['destroy'] = () => modalRef.destroy(); Object.assign(modalRef.instance, paramsModal); // 等待Modal内部的内容容器准备好后,再渲染动态组件 modalRef.instance.contentContainerReady.subscribe(() => { this.render.renderComponent(module, component, paramsComponent).subscribe((data) => { console.log(data); }); }); componentRef$.next(modalRef); componentRef$.complete(); }); return componentRef$; }
3. 确保RenderService的setModalReference正确赋值
确认RenderService的方法正确设置viewContainer:
@Injectable() export class RenderService { public viewContainer: ViewContainerRef; public injectors: Injector; private compiler: Compiler; // ...其他代码 public setModalReference(vcRef: ViewContainerRef): void { this.viewContainer = vcRef; } }
为什么这样能解决问题?
通过添加contentContainerReady Subject,我们确保了只有当StandardModalComponent内部的内容容器完全初始化、并且RenderService拿到正确的ViewContainerRef之后,才会触发动态组件的渲染操作,从根源上解决了异步顺序导致的undefined问题。
内容的提问来源于stack exchange,提问作者Sam Kelham
相关产品推荐
相关产品推荐

