如何检查 < ng-content > 是否为空

假设我有一个组件:

@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 的简单方法?

80838 次浏览

注入 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...
}

(未经测试)

在类似于 div的 HTML 元素中包装 ng-content以获得对它的本地引用,然后将 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>`

更新为角12; 旧逻辑(“ ref.nativeElement.childNodes.length”)会出错,因为 nativeElement现在是 undefined

“像素点”的回答中有一些缺失。我们不仅需要检查 children属性,因为父模板中的任何换行符或空格都会导致带有空白文本换行符的 children元素。 最好检查 .innerHTML.trim()它。

实例:

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

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

<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>

编辑17.03.2020

纯 CSS (2种解决方案)

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

可能的选择:

  1. 看这篇文章: 独生子女选择器

    这个版本需要更少的代码/标记

  2. :empty选择器,继续读。

    来自 IE9的支持,部分来自 IE7/8: https://caniuse.com/#feat=css-sel3

超文本标示语言

<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>

在我的例子中,我必须隐藏空 n- 内容的父级:

<span class="ml-1 wrapper">
<ng-content>
</ng-content>
</span>

简单的 css 工作:

.wrapper {
display: inline-block;


&:empty {
display: none;
}
}

我通过使用 @ ContentChildren装饰器实现了一个解决方案,这在某种程度上类似于@Lerner 的回答。

根据 医生的报道,这个装修师:

从内容 DOM 中获取元素或指令的 QueryList。每当添加、删除或移动一个子元素时,查询列表都会更新,并且查询列表可观察到的更改将生成一个新值。

所以父组件中的必要代码是:

<app-my-component>
<div #myComponentContent>
This is my component content
</div>
</app-my-component>

在组件类中:

@ContentChildren('myComponentContent') content: QueryList<ElementRef>;

然后,在组件模板中:

<div class="container">
<ng-content></ng-content>
<span *ngIf="*ngIf="!content.length""><em>Display this if ng-content is empty!</em></span>
</div>

完整示例 : < a href = “ https://stackblitz.com/edit/angle-jjjdqb”rel = “ nofollow norefrer”> https://stackblitz.com/edit/angular-jjjdqb

我发现这个解决方案是在角度组件中实现的,对于 matSuffix,在 表格-字段组件中。

在后来注入组件内容的情况下,之后应用程序被初始化,我们也可以使用反应实现,通过订阅 QueryListchanges事件:

export class MyComponentComponent implements AfterContentInit, OnDestroy {
private _subscription: Subscription;
public hasContent: boolean;


@ContentChildren('myComponentContent') content: QueryList<ElementRef>;


constructor() {}


ngAfterContentInit(): void {
this.hasContent = (this.content.length > 0);
this._subscription = this.content.changes.subscribe(() => {
// do something when content updates
//
this.hasContent = (this.content.length > 0);
});
}


ngOnDestroy() {
this._subscription.unsubscribe();
}


}

完整示例 : < a href = “ https://stackblitz.com/edit/angle-essvnq”rel = “ nofollow norefrer”> https://stackblitz.com/edit/angular-essvnq

如果你想显示一个默认的内容,为什么不直接使用 css 中的‘ only-child’选择器呢。

Https://developer.mozilla.org/en-us/docs/web/css/:only-child

例如: 超文本标示语言

<div>
<ng-content></ng-content>
<div class="default-content">I am default</div>
</div>

CSS

.default-content:not(:only-child) {
display: none;
}

对于角度10,它略有改变。你可以使用:

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

<ng-content #ref></ng-content>显示未声明错误“ ref”。 以下是角11(也可能是10) :

<div #ref><ng-content></ng-content></div>
<ng-container *ngIf="!ref.hasChildNodes()">
Default Content
</ng-container>

在角11中,我使用这个并且工作得很好。

模板档案:

 <h3 class="card-label" #titleBlock>
<ng-content select="[title]" ></ng-content>
</h3>

组成部分:

@ViewChild('titleBlock') titleBlock: ElementRef;
hasTitle: boolean;




ngAfterViewInit(): void {
if (this.titleBlock && this.titleBlock.nativeElement.innerHTML.trim().length > 0)
{
this.hasTitle=  true;
}
else
{
this.hasTitle=  false;
}
}

在角度12,控制台为我报告如下:

类型“ HTMLElement”上不存在“ nativeElement”属性

似乎存在一个特定的属性 childElementCount,您可以在这种情况下使用它。

因此,我成功地使用了它,它没有将动态内容封装到其他元素/标记中:

<div class="container">
<!-- some html skipped -->


<ng-container #someContent>
<ng-content></ng-content>
</ng-container>
<span
*ngIf="
someContent.childElementCount === undefined ||
someContent.childElementCount === 0
"
>
Display this if ng-content is empty!
</span>


<!-- some html skipped -->
</div>

二零二一年九月

如果没有实现组件提供的默认内容,还有另外一种技术可以通过使用 *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>;
}

然后在 HTML 模板中:

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


<ng-template #defaultTemplate>
<div class="default">
Default content...
</div>
</ng-template>

目标组成部分:

<!-- default content -->


<feature-component></feature-component>




<!-- dynamic content -->


<feature-component>
<ng-template #customTemplate>
<div> Custom group items. </div>
</ng-template>
</feature-component>

这个解决方案对我很有效(角度版本12.0.2)。 请注意,如果您的内容是动态的,并且在组件已经加载之后从空变为非空(或以其他方式) ,那么这可能不会起作用。 这可以通过在 ngOnChanges中添加更改 hasContent的代码来修复。

例如:

import {Component, ViewChild, AfterViewInit} from '@angular/core';


@Component({
selector: 'my-component',
template: '<div [ngClass]="[hasContent ? 'has-content' : 'no-content']">
<span #contentRef>
<ng-content></ng-content>
</span>
</div>']
})
export class Momponent implements AfterViewInit {


@ViewChild('contentRef', {static: false}) contentRef;


hasContent: boolean;


ngAfterViewInit(): void {
setTimeout(() => {
this.hasContent = this.contentRef?.nativeElement?.childNodes?.length > 1;
});
}
}