当ngIf为false时,将构建Angular4 ng-content

时间:2017-07-05 14:56:57

标签: html angular typescript angular2-template

我对新的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中的表现。

4 个答案:

答案 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组件,因为*ngIffalse。这意味着,没有调用它你不是实例化它,因此,不通过它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)

我最近也遇到了这个问题,但找到了与目前接受的解决方案不同的解决方案。

解决方案 (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

警告:由于这只是一个指令,它不应该做比在适当的时间实例化模板引用和可能指定一些 DI 提供程序更复杂的事情。例如,我会避免尝试在指令中应用样式或实例化复杂的组件树。如果我想创建一个列表组件,我可能会采用另一个答案中描述的 @ContentChild(TemplateRef) 方法;您将失去创建 <template> 的星号简写,但您将获得组件的全部功能。

我的问题

我的团队拥有一个应用程序,该应用程序是一个更大的网络应用程序的一部分,而其他应用程序则归其他团队所有。我们的组件假设它们可以注入一个 MyAppConfiguration 对象,但是这个对象只能在加载异步请求后才能注入。在我们的应用程序中,这不是问题:我们有一个“外壳”组件,在加载配置之前,它会将所有内容隐藏在 ngIf 后面。

问题是当其他团队想要引用我们的组件时。我们不希望他们每次都重复“等待配置加载”的逻辑,所以我尝试创建一个可以像这样使用的包装器组件:

<my-app-wrapper>
  <my-app-component></my-app-component>
</my-app-wrapper>

包装器注入一个服务对象并将其内容隐藏在 ngIf 后面,直到服务说配置已加载。

就像问题海报一样,我发现 ng-content 方法没有按预期工作:虽然内容正确地隐藏在 DOM 中,但 Angular 仍然实例化组件,导致依赖注入失败。

我确定的解决方案是将包装器组件重写为结构指令。