【问题标题】:Angular4 ng-content gets built when ngIf is false当 ngIf 为 false 时构建 Angular4 ng-content
【发布时间】: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 被构建然后存储起来,直到 *ngIfmy-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 中的情况。

【问题讨论】:

标签: html angular typescript angular2-template


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

【讨论】:

  • 我认为这不适用于我的情况。目的是:我有一个概览数据列表,在*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 使用 &lt;ng-template&gt;

      <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) 方法;您将失去创建 &lt;template&gt; 的星号简写,但您将获得组件的全部功能。

      我的问题

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

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2018-05-15
        • 1970-01-01
        • 2022-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-06
        • 1970-01-01
        • 2019-02-11
        相关资源
        最近更新 更多