默认的ViewContainerRef

如何在全局创建一个 ViewContainerRef?

Angular 中的 Component,Template 都得存在于 VD 中的一个 ViewContainer 里面。在基于 CDK 的 overlay 通过编程的方式来打开一个 template 的时候,需要指定一个 ViewContainerRef,每一次这个都是由调用者传递过来,但是,我们仅仅是用它存放一下 template, 而 template 的实际的 Dom 却是由我们自己接管,放置到顶层的。那么既然这个 ViewContainerRef,是这个作用,我们能否获取这么个默认的 ViewContainerRef 呢?

找到一个方案,就是编写一个组件,在组件内部明确的获取一个 ViewContainerRef,我们的代码只需要动态的创建这个组件就性了,而这个组件无需真正的挂到 Dom 上,只是存在于 VirtualDom 上。代码如下:

// 这个就是那个组件
@Component({
  selector: "ad-virtualContainer",
  template: `<ng-container #containerRef></ng-container>`,
})
export class AdVirtualViewContainer {
  @ViewChild("containerRef", { read: ViewContainerRef })
  viewContainer: ViewContainerRef;
}

let _internalViewContainerRef: ViewContainerRef = null;

export class DefaultViewContainerRefProps {
  appRef: ApplicationRef;
  factoryResolver: ComponentFactoryResolver;
  injector: Injector;
}

// 这个就是获取ViewContianerRef的代码。第一次的时候我们会动态创建组件,但是组件没有挂Dom树。
export function getDefaultViewContainerRef({
  appRef,
  factoryResolver,
  injector,
}: DefaultViewContainerRefProps): ViewContainerRef {
  if (_internalViewContainerRef === null) {
    const factory = factoryResolver.resolveComponentFactory(
      AdVirtualViewContainer
    );
    const compRef = factory.create(injector);
    compRef.changeDetectorRef.detectChanges();
    appRef.attachView(compRef.hostView);
    _internalViewContainerRef = compRef.instance.viewContainer;
  }
  return _internalViewContainerRef;
}

这个里面遇到个问题,当我们用 CDK 的 overlay 来弹框模板的时候,用上面的 ViewContainerRef 完全可以工作。但是,如果组件内部的 ViewContainerRef 是从构造函数注入的。那么这个时候,弹框能够打开,但是关闭的时候,模板的 DOM 没有销毁,一直没有找到原因。估计是在 cdk overlay 的关闭方式里面有问题。

介绍一个 ngZone.onStable 这个东西,这个东西官方文档里面说是所有的微任务执行完毕后会 emit 这个 Observable.

posted @ 2021-12-15 13:48  kongshu  阅读(119)  评论(0编辑  收藏  举报