如何检查 <ng-content> 是否为空?(到目前为止,在 Angular 2+ 中)

IT技术 javascript angular
2021-01-13 08:15:38

假设我有一个组件:

@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 {
}

现在,如果<ng-content>此组件为空,我想显示一些默认内容有没有一种简单的方法可以在不直接访问 DOM 的情况下做到这一点?

6个回答

包裹ng-content在像 a 这样的 HTML 元素中div以获取对它的本地引用,然后将ngIf表达式绑定ref.children.length == 0

template: `<div #ref><ng-content></ng-content></div> 
           <span *ngIf=" ! ref.children.length">
              Display this if ng-content is empty!
           </span>`

为 Angular 12 更新;老逻辑(” ref.nativeElement.childNodes.length“)给出了错误,因为nativeElementundefined现在。

有没有其他办法呢?因为这很难看,与 Aurelia 后备插槽相比
2021-03-19 08:15:38
在 Angular 问题跟踪器上有一个更好的方法的功能请求:github.com/angular/angular/issues/12530(可能值得在那里添加 +1)。
2021-03-23 08:15:38
使用更安全ref.children.lengthtext如果您使用空格或新行格式化 html,则childNodes 将包含节点,但 children 仍为空。
2021-04-08 08:15:38
我遵循了这个例子,它对我来说很好用。但我用!ref.hasChildNodes()而不是ref.nativeElement.childNodes.length == 0
2021-04-10 08:15:38
我正要发帖说这似乎不起作用,直到我意识到我使用了ref.childNodes.length == 0而不是ref.children.length == 0. 如果您可以编辑答案以保持一致,那将有很大帮助。简单的错误,不是抨击你。:)
2021-04-13 08:15:38

编辑 17.03.2020

纯 CSS(2 个解决方案)

如果没有任何内容投影到 ng-content 中,则提供默认内容。

可能的选择器:

  1. :only-child选择器。在这里看到这篇文章::only-child Selector

    这个需要更少的代码/标记。自 IE 9 起支持:Can I Use :only-child

  2. :empty选择器。只需进一步阅读。

    来自 IE 9 和部分自 IE 7/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>

CSS

.wrapper:not(:empty) + .default {
    display: none;
}

如果它不起作用

请注意,至少有一个空格被认为不是空的。Angular 会删除空格,但以防万一:

<div class="wrapper"><!--
    --><ng-content select="my-component"></ng-content><!--
--></div>

或者

<div class="wrapper"><ng-content select="my-component"></ng-content></div>
迄今为止最好最优雅的解决方案!谢啦!
2021-03-20 08:15:38
@BossOz 你是对的。它将被渲染(如果您有一个角度组件,将调用构造函数等)。此解决方案仅适用于愚蠢的视图。如果你有复杂的逻辑,涉及加载或类似的东西,我认为你最好的选择是编写一个结构指令,它可以获得一个模板/类(无论你想实现它)并根据逻辑,然后呈现所需组件或默认组件。评论部分对于示例来说太小了。我再次评论我们在我们的一个应用程序中的另一个例子。
2021-03-25 08:15:38
一种方法是让组件通过指令选择 ContentChildren(使用 DirectiveClass 查询,但用作结构指令,因此仅具有标记就不需要初始引导):<loader [data]="allDataWeNeed"> <desired-component *loadingRender><desired-component> <other-default-component *renderWhenEmptyResult></other-default-component> </loader>并且在加载组件中,您可以显示感知的性能加载,而正在加载数据。您可以用不同的方式编写/解决它。这是您如何解决它的一个演示。
2021-04-01 08:15:38
@Stefan 无论如何都会呈现默认内容......它只会被隐藏。因此,对于复杂的默认内容,这不是最佳解决方案。这是正确的吗?
2021-04-07 08:15:38
到目前为止,这是我的用例的最佳解决方案。我不记得我们有一个emptycss 伪类
2021-04-08 08:15:38

@pixelbits 答案中缺少一些内容。我们不仅需要检查children属性,因为父模板中的任何换行符或空格都会导致children元素出现空白文本\换行符。更好地检查.innerHTML.trim()它。

工作示例:

<span #ref><ng-content></ng-content></span>
<span *ngIf="!ref.innerHTML.trim()">
    Content if empty
</span>
*ngIf="!ref.innerHTML.trim()" 这部分会导致性能问题。修剪是 O(n)。
2021-03-20 08:15:38
@RandomCode 是对的,该函数将被多次调用。更好的方法是检查element.children.lengthelement.childnodes.length,具体取决于您要询问的内容。
2021-03-21 08:15:38
我们可以使用这个方法来检查子元素是否为空并隐藏父元素吗?我试过了,没有运气
2021-03-27 08:15:38
对我来说最好的答案:)
2021-03-29 08:15:38
@KavindaJayakody 是的,这将检查子元素是否为空。显示您的代码。
2021-04-05 08:15:38

当您注入内容时,添加一个引用变量:

<div #content>Some Content</div>

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

@ContentChild('content') content: ElementRef;

所以在你的组件模板中,你可以检查 content 变量是否有一个值

<div>
  <ng-content></ng-content>
  <span *ngIf="!content">
    Display this if ng-content is empty!
  </span>    
</div> 
OP 询问 ngContent 是否为空 - 这意味着<MyContainer></MyContainer>. 您的解决方案要求用户在 MyContainer: 下创建一个子元素<MyContainer><div #content></div></MyContainer>虽然这是一种可能性,但我不会说这是优越的。
2021-03-17 08:15:38
这是最干净,更好的答案。它支持开箱即用的 ContentChild API。无法理解为什么最高答案得到这么多票。
2021-03-26 08:15:38

注射elementRef: ElementRef并检查是否elementRef.nativeElement有儿童。这可能只适用于encapsulation: ViewEncapsulation.Native.

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

<div #contentWrapper>
  <ng-content></ng-content>
</div>

并检查它是否有任何孩子

@ViewChild('contentWrapper') contentWrapper;

ngAfterViewInit() {
  contentWrapper.nativeElement.childNodes...
}

(未测试)

Gunter,你提出了两个非常好的观点。Angular 不应该因为 AngularJS 的方式而带有污名。但我要指出,第一点(以及我在上面提出的那些)落入了工程学的哲学沟壑。也就是说,我已经成为软件耦合方面的专家,我建议不要 [至少大量] 使用@ViewChild. 感谢您在我的评论中添加一些平衡——我发现我们的两个评论与另一个一样值得考虑。
2021-03-14 08:15:38
由于使用了@ViewChild. 虽然“合法”,但不应使用 ViewChild 访问模板中的子节点。这是一个反模式,因为虽然父母可能知道关于孩子,他们不应该夫妇到他们,因为它通常会导致病理耦合; 更合适的数据传输或请求处理形式是使用事件驱动方法
2021-03-18 08:15:38
不在 API 中公开这一点才是真正的反模式 :-(
2021-03-22 08:15:38
@Cody 感谢您对您的反对票发表评论。其实我不听你的论点。模板和组件类是一个单元——一个组件。我不明白为什么从代码访问模板应该是一种反模式。Angular (2/4) 与 AngularJS 几乎没有任何共同之处,除了两者都是 Web 框架和名称。
2021-03-25 08:15:38
@Cody 也许你的强烈意见@ViewChild()来自这个名字。“孩子”不一定是孩子,他们只是组件的声明部分(如我所见)。如果为此标记创建的组件实例是访问,这当然是另一回事,因为它至少需要了解它们的公共接口,而这实际上会创建紧密耦合。这是一个非常有趣的讨论。让我担心的是,我没有足够的时间来考虑这些问题,因为使用这些框架时,时间常常被浪费掉,根本找不到任何方法。
2021-03-27 08:15:38