在Angular 2中如何检查<ng-content>是否为空?

Sla*_*das 70 javascript angular

假设我有一个组件:

@Component({
    selector: 'MyContainer',
    template: `
    <div class="container">
        <!-- some html skipped -->
        <ng-content></ng-content>
        <span *ngIf="????">Display this if ng-content is empty!</span>
        <!-- some html skipped -->
    </div>`
})
export class MyContainer {
}
Run Code Online (Sandbox Code Playgroud)

现在,如果<ng-content>此组件为空,我想显示一些默认内容.有没有直接访问DOM的简单方法?

pix*_*its 76

包装ng-content一个HTML元素,如div获取对它的本地引用,然后将ngIf表达式绑定到ref.children.length == 0:

template: `<div #ref><ng-content></ng-content></div> 
           <span *ngIf="ref.nativeElement.childNodes.length == 0">
              Display this if ng-content is empty!
           </span>`
Run Code Online (Sandbox Code Playgroud)

  • 有没有替代方法吗?因为与Aurelia后备插槽相比,这是丑陋的 (16认同)
  • 使用`ref.children.length`更安全.如果使用空格或换行格式化html,childNodes将包含`text`节点,但子节点仍为空. (12认同)
  • 我遵循了这个例子,它对我来说很好用。但我使用了 `!ref.hasChildNodes()` 而不是 `ref.nativeElement.childNodes.length == 0` (6认同)
  • 在角度问题跟踪器上有一个更好的方法的功能请求:https://github.com/angular/angular/issues/12530(可能值得在那里添加+1). (5认同)
  • 我想发布这个似乎没有工作,直到我意识到我使用`ref.childNodes.length == 0`而不是`ref.children.length == 0`的例子.如果您可以编辑答案以保持一致,那么这将有所帮助.容易犯错误,不要抨击你.:) (4认同)
  • 它甚至是`ref.nativeElement.childNodes.length`.请问这个答案可以编辑吗? (2认同)
  • 如果您的 ng-content 中有 *ngIfs 或任何条件,这将不起作用,因为 Angular 会在其位置添加评论,以计算孩子的长度 (2认同)

小智 26

在@pixelbits答案中有一些缺失.我们不仅需要检查children属性,因为父模板中的任何换行符或空格都会导致children带有空白text\linebreaks的元素.更好地检查.innerHTML.trim()它.

工作范例:

<span #ref><ng-content></ng-content></span>
<span *ngIf="!ref.innerHTML.trim()">
    Content if empty
</span>
Run Code Online (Sandbox Code Playgroud)

  • @RandomCode是对的,该函数将被多次调用。更好的方法是检查“element.children.length”或“element.childnodes.length”,具体取决于您想要要求的内容。 (2认同)

Ler*_*ner 17

注入内容时,请添加参考变量:

<div #content>Some Content</div>
Run Code Online (Sandbox Code Playgroud)

并在您的组件类中使用@ContentChild()获得对注入内容的引用

@ContentChild('content') content: ElementRef;
Run Code Online (Sandbox Code Playgroud)

因此,在组件模板中,您可以检查content变量是否具有值

<div>
  <ng-content></ng-content>
  <span *ngIf="!content">
    Display this if ng-content is empty!
  </span>    
</div> 
Run Code Online (Sandbox Code Playgroud)

  • OP询问ngContent是否为空-这意味着`&lt;MyContainer&gt; &lt;/ MyContainer&gt;。您的解决方案希望用户在MyContainer下创建一个子元素:“ &lt;MyContainer&gt; &lt;div #content&gt; &lt;/ div&gt; &lt;/ MyContainer&gt;”。虽然这是可能的,但我不会说这是优越的。 (5认同)

Ste*_*ein 12

这是一个CSS解决方案.如果在ng-content中没有设置,则可以提供默认组件.因为他们是我的兄弟姐妹,所以可以像这样解决它.

从IE 9开始兼容,部分自IE7/8以来兼容:https://caniuse.com/#feat=css-sel3

HTML

<div class="wrapper">
    <ng-content select="my-component"></ng-content>
</div>
<div class="default">
    This shows something default.
</div>
Run Code Online (Sandbox Code Playgroud)

CSS

.wrapper:not(:empty) + .default {
    display: none;
}
Run Code Online (Sandbox Code Playgroud)

  • 到目前为止,这是适合我的用例的最佳解决方案。我不记得我们有一个“空”CSS 伪类 (6认同)
  • @BossOz你是对的。它将被渲染(如果您有角度组件,将调用构造函数等)。该解决方案仅适用于愚蠢的视图。如果您有复杂的逻辑,涉及加载或类似的东西,我认为您最好的选择是编写一个结构指令,它可以获取模板/类(但是您想实现它)并根据逻辑,然后渲染所需组件或默认组件。评论部分对于示例来说太小了。我再次评论我们在一个应用程序中的另一个例子。 (2认同)

Rav*_*and 12

2021 年 9 月

如果实现组件没有提供默认内容,还有另一种技术可以通过使用*ngTemplateOutlet指令来完成默认内容,这允许我们对自定义进行更多控制:

源组件中的示例:

import { Component, ContentChild, TemplateRef } from '@angular/core';
@Component({
  selector: 'feature-component',
  templateUrl: './feature-component.component.html',
})
export class FeatureComponent {
  @ContentChild('customTemplate') customTemplate: TemplateRef<any>;
}
Run Code Online (Sandbox Code Playgroud)

然后在 HTML 模板中:

<ng-container
  [ngTemplateOutlet]="customTemplate || defaultTemplate"
></ng-container>

<ng-template #defaultTemplate>
  <div class="default">
    Default content...
  </div>
</ng-template>
Run Code Online (Sandbox Code Playgroud)

目标组件:

<!-- default content -->

<feature-component></feature-component>


<!-- dynamic content -->

<feature-component>
  <ng-template #customTemplate>
    <div> Custom group items. </div>
  </ng-template>
</feature-component>
Run Code Online (Sandbox Code Playgroud)

  • 如果没有从外部控件传入任何内容,这是唯一可以让我完全删除元素的解决方案。我能够执行`&lt;mat-dialog-actions *ngIf="actionsTemplate"&gt;&lt;ng-container [ngTemplateOutlet]="actionsTemplate"&gt;&lt;/ng-container&gt;&lt;/mat-dialog-actions&gt;`。现在,如果没有内容,“mat-dialog-actions”就会消失,而不是空着并占用空间。 (2认同)

eco*_*m31 9

如果您想显示默认内容,为什么不使用 css 中的“only-child”选择器。

https://developer.mozilla.org/en-US/docs/Web/CSS/:only-child

例如:HTML

<div>
  <ng-content></ng-content>
  <div class="default-content">I am default</div>
</div>
Run Code Online (Sandbox Code Playgroud)

css

.default-content:not(:only-child) {
   display: none;
}
Run Code Online (Sandbox Code Playgroud)


Gün*_*uer 6

注入elementRef: ElementRef并检查是否elementRef.nativeElement有孩子.这可能只适用于encapsulation: ViewEncapsulation.Native.

包裹<ng-content>标签并检查它是否有孩子.这不起作用encapsulation: ViewEncapsulation.Native.

<div #contentWrapper>
  <ng-content></ng-content>
</div>
Run Code Online (Sandbox Code Playgroud)

并检查它是否有孩子

@ViewChild('contentWrapper') contentWrapper;

ngAfterViewInit() {
  contentWrapper.nativeElement.childNodes...
}
Run Code Online (Sandbox Code Playgroud)

(未测试)

  • @Cody感谢你发表评论到你的downvote.其实我不遵循你的论证.模板和组件类是一个单元 - 一个组件.我不明白为什么从代码访问模板应该是反模式.Angular(2/4)与AngularJS几乎没有任何共同之处,只是两者都是Web框架和名称. (4认同)
  • 由于使用了`@ ViewChild`,我对此进行了低估.在"Legal"时,ViewChild不应用于访问模板中的子节点.这是一个反模式,因为虽然父母可能知道关于孩子,但他们不应该*夫妻*,因为它通常导致**病理耦合**; 更合适的数据传输或请求处理形式是使用**事件驱动的方法**. (3认同)
  • 冈特,你做了两个非常好的观点.Angular不一定会受到AngularJS方式的耻辱.但我要说的是,第一点(以及我上面提到的那些)落在了工程学的哲学阴沟上.也就是说,我已成为软件耦合方面的专家,我建议不要[至少重度]使用`@ ViewChild`.感谢您为我的评论添加一些平衡 - 我发现我们的评论与其他评论一样值得考虑. (2认同)
  • @Cody或许你对`@ViewChild()`的强烈看法来自这个名字."孩子"不一定是孩子,他们只是组件的声明部分(我认为).如果为此标记创建的组件实例是访问,这当然是一个不同的故事,因为它需要至少知道它们的公共接口,这实际上会产生紧密耦合.这是一个非常有趣的讨论.令我担心的是,我没有足够的时间来考虑这些问题,因为有了这样的框架,往往会浪费时间来找到任何方式. (2认同)
  • 不在API中暴露这是真正的反模式:-( (2认同)
  • @Mozgor 因为我想要包装元素。对于 `ContentChild` 或 `ContentChildren` 我不知道要使用哪个选择器。这就是为什么我在当前组件中添加包装器,然后检查投影的子组件是否已添加到其中。 (2认同)