我对新的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
组件中(my-wrapper.component.html
):
<ng-container *ngIf="false">
<ng-content></ng-content>
</ng-container>
我总是看到console.log()
输出。我想,my-component
会被构建,然后存储起来,直到*ngIf
变为true
my-wrapper
。{/ p>
目的是建立一个通用的“列表项+细节”组件。假设我有一个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>
MY-wrapper.component.html:
<div (click)="toggleDetail()">Click for more</div>
<div *ngIf="showDetail">
<ng-content></ng-content>
</div>
有没有办法告诉Angular,在有必要将其添加到页面之前忽略已转换的内容?类似于它在AngularJS中的表现。
答案 0 :(得分:7)
根据@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中这么容易做到这一点。
答案 1 :(得分: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>
答案 2 :(得分:0)
这是因为Ng内容是在构建时发生的,当您传递内容时,实际上并不会使用ngIf指令删除或重新创建它。只能移动它并实例化该组件。
答案 3 :(得分:0)
我最近也遇到了这个问题,但找到了与目前接受的解决方案不同的解决方案。
(解决方案适用于 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
),但您可以通过使用作为函数的输入来模拟输出。
警告:由于这只是一个指令,它不应该做比在适当的时间实例化模板引用和可能指定一些 DI 提供程序更复杂的事情。例如,我会避免尝试在指令中应用样式或实例化复杂的组件树。如果我想创建一个列表组件,我可能会采用另一个答案中描述的 @ContentChild(TemplateRef)
方法;您将失去创建 <template>
的星号简写,但您将获得组件的全部功能。
我的团队拥有一个应用程序,该应用程序是一个更大的网络应用程序的一部分,而其他应用程序则归其他团队所有。我们的组件假设它们可以注入一个 MyAppConfiguration
对象,但是这个对象只能在加载异步请求后才能注入。在我们的应用程序中,这不是问题:我们有一个“外壳”组件,在加载配置之前,它会将所有内容隐藏在 ngIf
后面。
问题是当其他团队想要引用我们的组件时。我们不希望他们每次都重复“等待配置加载”的逻辑,所以我尝试创建一个可以像这样使用的包装器组件:
<my-app-wrapper>
<my-app-component></my-app-component>
</my-app-wrapper>
包装器注入一个服务对象并将其内容隐藏在 ngIf
后面,直到服务说配置已加载。
就像问题海报一样,我发现 ng-content
方法没有按预期工作:虽然内容正确地隐藏在 DOM 中,但 Angular 仍然实例化组件,导致依赖注入失败。
我确定的解决方案是将包装器组件重写为结构指令。