为啥使用 ViewContainerRef 而不是 *ngif?

Posted

技术标签:

【中文标题】为啥使用 ViewContainerRef 而不是 *ngif?【英文标题】:Why use ViewContainerRef over *ngif?为什么使用 ViewContainerRef 而不是 *ngif? 【发布时间】:2018-03-22 01:27:25 【问题描述】:

我只能这样做

<my-awesome-component *ngIf="ConditionToIncludeComponent"></my-awesome-component>

但是每个关于在dom中动态插入组件的文档都是基于ViewContainerRef。我喜欢它的作用。但是是什么让它比 *ngif 如此特别?

只需指出两者的优缺点。请。 谢谢!

【问题讨论】:

【参考方案1】:

TLDR;

如果您不知道在组合此模板时将在组件模板中使用什么组件,请使用viewContainerRef。如果您事先知道该组件但有时可以隐藏,请使用ngIf

说明

ViewContainerRef用于指定动态组件的插入点。使用ngIf时需要提前指定html中的组件。因此,如果您有插入三个组件之一的位置,则需要执行以下操作:

<my-awesome-component1 *ngIf="ConditionToIncludeComponent1"></my-awesome-component1>
<my-awesome-component2 *ngIf="ConditionToIncludeComponent2"></my-awesome-component2>
<my-awesome-component3 *ngIf="ConditionToIncludeComponent3"></my-awesome-component3>

而对于viewContainerRef,您只需要一个位置(通常使用`ng-container 指定)。使用ngComponentOutlet 可以这样做:

template: `<ng-container ngComponentOutlet="componentToInsert"></ng-container>`

class MyComponent 

   const myAwesomeComponent1 = cfr.resolveComponentFactory(MyAwesomeComponent1);
   const myAwesomeComponent2 = cfr.resolveComponentFactory(MyAwesomeComponent1);
   const myAwesomeComponent3 = cfr.resolveComponentFactory(MyAwesomeComponent1);
       
    if (ConditionToIncludeComponent1) 
        componentToInsert = myAwesomeComponent1;
    else if (ConditionToIncludeComponent2) 
        componentToInsert = myAwesomeComponent2;
    else if (ConditionToIncludeComponent3) 
        componentToInsert = myAwesomeComponent3;

或者使用createComponent方法手动组件:

template: `<ng-container #spot></ng-container>`

class MyComponent 
   @ViewChild('spot', read: ViewContainerRef) vc;

   const myAwesomeComponent1 = cfr.resolveComponentFactory(MyAwesomeComponent1);
   const myAwesomeComponent2 = cfr.resolveComponentFactory(MyAwesomeComponent1);
   const myAwesomeComponent3 = cfr.resolveComponentFactory(MyAwesomeComponent1);
       
    if (ConditionToIncludeComponent1) 
        vc.createComponent(myAwesomeComponent1);
    else if (ConditionToIncludeComponent2) 
        vc.createComponent(myAwesomeComponent2);
    else if (ConditionToIncludeComponent3) 
        vc.createComponent(myAwesomeComponent3);

除了不便和臃肿的 html 模板之外,ngIf 方法的更大问题是性能影响,因为三个 ngIf 指令必须在每个更改检测周期执行一些逻辑。

更多信息请阅读:

Exploring Angular DOM manipulation techniques using ViewContainerRef

【讨论】:

这是否意味着如果我使用 ngIf,组件已经存在但隐藏了?我一定会检查你的文章。您能否就我的问题发表另一篇文章。我想很多人在互联网上搜索它。谢谢! @Prabesh,不,ngIf 如果评估结果为 false,则不会创建组件。但是将创建 ngIf 指令的实例。在变更检测期间,Angular 将在ngIf 的每个实例上花费一些时间。如果您不知道将组件模板放在一起时将使用什么组件。如果您确实知道该组件但有时可以隐藏它,请使用ngIf。我会考虑文章建议,谢谢 我很确定我将从现在开始使用 ViewContainerRef。感谢您花时间解决我的问题。目前,我想为单击评论按钮后将加载前 5 个 cmets 的图片创建一个评论部分。最后是一个评论框。很明显,我必须使用 ngFor 来加载 cmets。那么,您如何建议我应该将 ngFor 和 ViewContainerRef 结合起来使其工作。这意味着我需要另一个 用于该评论框,对吗? @Prabesh,您能否再创建一个问题并在此处发布链接? @MaxWizardK 说,如果我显示一个包含多行的 html 表(使用 *ngFor 生成),并在每行悬停时显示一个工具提示组件。所以我应该使用 *ngIf 像 &lt;tr *ngFor="let subitemObj of myArray"&gt;&lt;app-tooltip-comp *ngIf='subitemObj.showTooltip'&gt;&lt;/app-tooltip-comp&gt;&lt;/tr&gt; ,或者我应该使用viewcontainerRef 在每次悬停在tr 标签上时动态插入此组件。

以上是关于为啥使用 ViewContainerRef 而不是 *ngif?的主要内容,如果未能解决你的问题,请参考以下文章

使用 ViewContainerRef 探索Angular DOM操作

Angular获取动态创建的组件的viewContainerRef

无法读取未定义的属性“viewContainerRef”

如何在没有预先确定的 ViewContainerRef 的情况下向 DOM 动态添加角度组件?

Angular 2:如何动态创建 ViewContainerRef

typescript ViewContainerRef