当 ngIf 为 false 时构建 Angular4 ng-content
Posted
技术标签:
【中文标题】当 ngIf 为 false 时构建 Angular4 ng-content【英文标题】:Angular4 ng-content gets built when ngIf is false 【发布时间】:2017-12-09 07:53:30 【问题描述】:我对新的ng-content
嵌入有疑问。
假设我有一个组件my-component
,它在其ngOnInit()
函数中执行一些繁重的负载操作(目前,只是一个console.log()
)。
我有一个包装器,它通过嵌入 (my-wrapper.component.html
) 显示内容。
<ng-content></ng-content>
如果我这样设置环境,日志语句不会显示:
<my-wrapper *ngIf="false">
<my-component></my-component>
</my-wrapper>
我假设,my-wrapper
组件没有构建,所以内容被忽略了。
但如果我尝试将逻辑移动到像这样 (my-wrapper.component.html
) 的 my-wrapper
组件中:
<ng-container *ngIf="false">
<ng-content></ng-content>
</ng-container>
我总是看到console.log()
输出。我猜,my-component
被构建然后存储起来,直到 *ngIf
在 my-wrapper
中变为 true
。
目的是构建一个通用的“列表项+详细信息”组件。假设我有一个 N 个概览元素 (my-wrapper
) 的列表,它们在 *ngFor
循环中呈现。这些元素中的每一个都有自己的详细信息组件 (my-component
),一旦我决定向特定项目显示更多信息,它就会加载自己的数据。
overview.html:
<ng-container *ngFor="let item of items">
<my-wrapper>
<my-component id="item.id"></my-component>
</my-wrapper>
</ng-container>
我的包装器.component.html:
<div (click)="toggleDetail()">Click for more</div>
<div *ngIf="showDetail">
<ng-content></ng-content>
</div>
有没有办法告诉 Angular,在需要添加到页面之前忽略转入的内容?类似于 AngularJS 中的情况。
【问题讨论】:
我建议不要在包装器上添加条件,而是将条件添加到子组件以停止执行 ***.com/questions/41652919/… 【参考方案1】:通过这样做:
<my-wrapper *ngIf="false">
<my-component></my-component>
</my-wrapper>
您没有调用MyComponent
组件,因为*ngIf
是false
。这意味着,不调用它就不会实例化它,因此不会通过它的ngOnInit
。这就是你没有得到控制台日志的原因。
通过这样做:
<ng-container *ngIf="false">
<ng-content></ng-content>
</ng-container>
您在组件内部,您只是在限制要在模板中呈现的内容,但您已经实例化了您的组件,因此您通过了 ngOnInit
并完成了控制台日志。
如果您想限制某些东西(使用选择器或ng-content
甚至div
的组件调用),直到您有一些可用数据,您可以执行以下操作:
datasLoaded: Promise<boolean>;
this.getData().subscribe(
(data) =>
this.datasLoaded = Promise.resolve(true); // Setting the Promise as resolved after I have the needed data
);
在你的模板中:
<ng-container *ngIf="datasLoaded | async">
// stuff here
</ng-container>
或者:
<my-component *ngIf="datasLoaded | async">
// Didn't test this one, but should follow the same logic. If it doesn't, wrap it and add the ngIf to the wrapper
</my-component>
【讨论】:
我认为这不适用于我的情况。目的是:我有一个概览数据列表,在*ngFor
-loop (my-wrapper
) 中呈现。对于每个概览项,我都有第二个“详细信息”组件 (my-component
),一旦我切换父项 (*ngIf
),它应该加载自己的数据(通过概览项的 ID)。如果我尝试您的方法,所有细节都会立即触发,从而导致数百次昂贵的 API 调用。我想我可以将my-component
更多地集成到my-wrapper
中,但这会使其不那么通用,这令人失望。【参考方案2】:
根据@nsinreal 的评论,我找到了答案。我觉得有点深奥,所以我想在这里发布它:
答案是使用ng-template
和*ngTemplateOutlet
。
在my-wrapper
组件中,这样设置模板(my-wrapper.component.html
):
<div (click)="toggleDetail()">Click for more</div>
<div *ngIf="showDetail" [hidden]="!isInitialized">
<ng-container *ngTemplateOutlet="detailRef"></ng-container>
</div>
请注意,[hidden]
并不是真正需要的,它会隐藏孩子的“原始”模板,直到它决定加载完成。只要确保,不要把它放在*ngIf
中,否则*ngTemplateOutlet
将永远不会被触发,导致什么都不会发生。
要设置detailRef
,请将其放入组件代码中(my-wrapper.component.ts
):
import ContentChild, TemplateRef from '@angular/core';
@Component( ... )
export class MyWrapperComponent
@ContentChild(TemplateRef) detailRef;
...
现在,您可以像这样使用包装器:
<my-wrapper>
<ng-template>
<my-component></my-component>
</ng-template>
</my-wrapper>
我不确定,为什么它需要如此复杂的“变通办法”,而以前在 AngularJS 中很容易做到这一点。
【讨论】:
【参考方案3】:这是因为 Ng 内容发生在构建时,当你传递内容时,它实际上并没有被 ngIf 指令删除或重新创建。它只是被移动并且组件被实例化。
【讨论】:
【参考方案4】:我最近也遇到了这个问题,但选择了与当前接受的解决方案不同的解决方案。
解决方案 (TL;DR)
(解决方案适用于 AngularDart;我认为它在 Angular 中很相似)
使用结构指令;教程链接如下。
代替:
<my-wrapper>
<my-contents></my-contents>
</my-wrapper>
你的用法变成:
<div *myWrapper>
<my-contents></my-contents>
</div>
这是以下内容的简写(在 AngularDart 中;我认为 Angular 使用 <ng-template>
)
<template myWrapper>
<div>
<my-contents></my-contents>
</div>
</template>
MyWrapper
指令逻辑与NgIf
类似,只是它有自己的逻辑来计算条件。以下两个教程都解释了如何创建类似NgIf
的指令以及如何使用特殊的微语法(例如*myWrapper="myInput: expression"
)将您自己的输入传递给它。请注意,微语法不支持输出 (@Output
),但您可以使用作为函数的输入来模拟输出。
Tutorial for Angular
Tutorial for AngularDart
警告:由于这只是一个指令,它不应该做任何比在适当的时间实例化模板 ref 并可能指定一些 DI 提供程序更复杂的事情。例如,我会避免尝试在指令中应用样式或实例化复杂的组件树。如果我想创建一个列表组件,我可能会采用另一个答案中描述的@ContentChild(TemplateRef)
方法;您将失去创建 <template>
的星号简写,但您将获得组件的全部功能。
我的问题
我的团队拥有一个应用程序,该应用程序是一个更大的网络应用程序的一部分,其他应用程序由其他团队拥有。我们的组件假设它们可以注入一个MyAppConfiguration
对象,但这个对象只能在加载异步请求后才能注入。在我们的应用中,这不是问题:我们有一个“shell”组件,它在配置加载之前将所有内容隐藏在 ngIf
后面。
问题是当其他团队想要引用我们的组件时。我们不希望他们每次都重复“等待直到加载配置”逻辑,所以我尝试创建一个可以像这样使用的包装器组件:
<my-app-wrapper>
<my-app-component></my-app-component>
</my-app-wrapper>
包装器注入一个服务对象并将其内容隐藏在ngIf
后面,直到服务说配置已加载。
就像问题海报一样,我发现ng-content
方法不能按预期工作:虽然内容正确地从 DOM 中隐藏了,但 Angular 仍然实例化导致依赖注入失败的组件。
我决定的解决方案是将包装器组件重写为结构指令。
【讨论】:
以上是关于当 ngIf 为 false 时构建 Angular4 ng-content的主要内容,如果未能解决你的问题,请参考以下文章
离子 - 当标签 <form> 中有 *ngIf 时无法获取离子输入的值
Angular 5 自定义指令触发包装到 *ngIf=false 中的元素