Angular 2で<ng-content>が空かどうかを確認する方法は?


92

コンポーネントがあるとします。

@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に直接アクセスせずにこれを行う簡単な方法はありますか?



参考までに、受け入れられた回答が機能することは知っていますが、「useDefault」タイプの入力パラメーターをコンポーネントに渡す方が良いスタイルだと思います。デフォルトはfalseです。
bryan60 2017年

回答:


97

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

23
これに代わる方法はありませんか?これは醜いので、Aureliaフォールバックスロットと比較して
宇宙飛行士

18
を使用する方が安全ref.children.lengthです。textHTMLをスペースまたは新しい行でフォーマットすると、childNodesにノードが含まれますが、子は空のままです。
議会

5
Angular Issue Trackerには、より良い方法を求める機能リクエストがあります:github.com/angular/angular/issues/12530(そこに+1を追加する価値があるかもしれません)。
eppsilon 2017年

5
ref.childNodes.length == 0代わりにの例を使用していることに気付くまで、これは機能していないようだと投稿しようとしていましたref.children.length == 0。一貫性のある回答を編集できれば、たくさんの助けになります。簡単な間違い、あなたをバッシングしないでください。:)
ダスティンクリーブランド

3
私はこの例に従いました、そしてそれは私にとってうまく働きます。しかし、私は!ref.hasChildNodes()代わりに使用しましたref.nativeElement.childNodes.length == 0
SergeyDzyuba19年

34

@pixelbitsの回答にいくつか欠けているものがあります。children親テンプレートに改行やスペースがあると、children要素に空白のtext \ linebreakが含まれるため、プロパティだけでなくチェックする必要があります。確認.innerHTML.trim()てください。

実例:

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

1
私にとってのベストアンサー:)
KamilKiełczewski2018

このメソッドを使用して、子要素が空であるかどうかを確認し、空である場合は親を非表示にできますか?私は試しましたが、運がありません
KavindaJayakody19年

@KavindaJayakodyはい、これは子要素が空かどうかをチェックします。コードを表示します。
lfoma

* ngIf = "!ref.innerHTML.trim()"この部分は、パフォーマンスの問題を引き起こします。トリムはO(n)です。
RandomCode

1
@RandomCodeは正しいです、関数は複数回呼び出されます。より良い方法は、あなたが何を求めたいelement.children.lengthelement.childnodes.lengthに応じて、またはをチェックすることです。
Stefan Rein

31

2020年3月17日編集

純粋なCSS(2つのソリューション)

ng-contentに何も投影されない場合、デフォルトのコンテンツを提供します。

可能なセレクター:

  1. :only-childセレクタ。こちらの投稿をご覧ください:: only-child Selector

    これはより少ないコード/マークアップを必要とします。IE 9以降のサポート:: only-childを使用できますか

  2. :emptyセレクタ。さらに読んでください。

    IE9および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>

CSS

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

それが機能していない場合

少なくとも1つの空白があると、空ではないと見なされることに注意してください。Angularは空白を削除しますが、そうでない場合に備えて:

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

または

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

1
これは私のユースケースにとって断然最良の解決策でした。emptycss
疑似クラス

@Stefanとにかくデフォルトのコンテンツがレンダリングされます...非表示にするだけです。したがって、複雑なデフォルトコンテンツの場合、これは最善の解決策ではありません。これは正解?
BossOz

1
@BossOzその通りです。レンダリングされます(角度コンポーネントがある場合はコンストラクターなどが呼び出されます)。このソリューションは、ダムビューにのみ有効です。ロードなどを含む複雑なロジックがある場合、私の意見では、テンプレート/クラスを取得できる構造ディレクティブを作成し(ただし、実装する必要があります)、ロジックに応じてレンダリングすることをお勧めします。目的のコンポーネントまたはデフォルトのコンポーネント。コメントセクションは、例としては小さすぎます。私たちのアプリの1つにある別の例についてもう一度コメントします。
Stefan Rein

1つの方法は、コンポーネントがディレクティブを介してContentChildrenを選択することです(DirectiveClassを使用したクエリですが、構造ディレクティブとして使用されるため、マークアップを取得するだけで最初のブートストラップは必要ありません)。<loader [data]="allDataWeNeed"> <desired-component *loadingRender><desired-component> <other-default-component *renderWhenEmptyResult></other-default-component> </loader>ロードコンポーネントでは、知覚されるパフォーマンスのロードを表示できます。データが読み込まれています。あなたはそれをさまざまな方法で書く/解決することができます。これは、それを解決する方法の1つのデモンストレーションです。
Stefan Rein

21

コンテンツを挿入するときに、参照変数を追加します。

<div #content>Some Content</div>

コンポーネントクラスで、@ ContentChild()を使用して挿入されたコンテンツへの参照を取得します

@ContentChild('content') content: ElementRef;

したがって、コンポーネントテンプレートで、コンテンツ変数に値があるかどうかを確認できます

<div>
  <ng-content></ng-content>
  <span *ngIf="!content">
    Display this if ng-content is empty!
  </span>    
</div> 

これは最もクリーンで、はるかに良い答えです。すぐに使用できるContentChildAPIをサポートします。なぜトップアンサーがこれほど多くの票を獲得したのか理解できません。
CarbonDry 2018年

10
OPは、ngContentが空かどうかを尋ねました-これはを意味し<MyContainer></MyContainer>ます。このソリューションでは、ユーザーがMyContainerの下にサブ要素を作成することを想定しています<MyContainer><div #content></div></MyContainer>。これは可能性ですが、これが優れているとは言えません。
pixelbits 2018年

8

注入elementRef: ElementRefelementRef.nativeElementて、子供がいるかどうかを確認します。これはでのみ機能する可能性がありencapsulation: ViewEncapsulation.Nativeます。

<ng-content>タグをラップして、子があるかどうかを確認します。これはでは機能しませんencapsulation: ViewEncapsulation.Native

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

子供がいるかどうかを確認します

@ViewChild('contentWrapper') contentWrapper;

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

(未検証)


3
を使用しているため、これに反対しました@ViewChild。「リーガル」の間、ViewChildをテンプレート内の子ノードへのアクセスに使用しないでください。親がかもしれないが、これはアンチパターンである知っている子供たち、彼らはいけないカップルが、通常につながるとしてそれらへの病理学的カップリング; データ転送または要求処理のより適切な形式は、イベント駆動型の方法論を使用することです。
コーディ

5
@Codyは、反対票にコメントを投稿していただきありがとうございます。実際、私はあなたの議論に従わない。テンプレートとコンポーネントクラスは単一のユニット、つまりコンポーネントです。コードからテンプレートにアクセスすることがアンチパターンである理由がわかりません。Angular(2/4)は、両方がWebフレームワークであり、名前であることを除いて、AngularJSとほとんど共通点がありません。
ギュンターZöchbauer

2
ガンター、あなたは2つの本当に良い点を指摘します。Angularは、必ずしもAngularJSの方法からの汚名で孵化する必要はありません。しかし、最初のポイント(および私が上で述べたポイント)は、エンジニアリングの哲学的な溝に向かっていることを示します。そうは言っても、私はソフトウェアカップリングの専門家になり、[少なくとも大量の]を使用しないようにアドバイスしたいと思い@ViewChildます。コメントにバランスを追加していただきありがとうございます。どちらのコメントも、もう一方のコメントと同じように検討する価値があります。
コーディ

2
@Codyおそらくあなたの強い意見@ViewChild()は名前から来ています。「子」は必ずしも子ではなく、コンポーネントの宣言部分にすぎません(私が見ているように)。このマークアップ用に作成されたコンポーネントインスタンスがアクセスである場合、これはもちろん別の話です。少なくともそれらのパブリックインターフェイスに関する知識が必要であり、実際には緊密な結合が作成されるためです。これは非常に興味深い議論です。そのようなフレームワークでは、方法を見つけるのに時間がかかりすぎることが多いので、そのような問題について考える十分な時間がないのではないかと心配しています。
ギュンターZöchbauer

3
これをAPIで公開しないのは、本当のアンチパターンです:-(
Simon_Weaver 2018年

4

デフォルトのコンテンツを表示したい場合は、cssの「only-child」セレクターを使用しないでください。

https://www.w3schools.com/cssref/sel_only-child.asp

例:HTML

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

css

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

コンポーネント内のViewChildやその他のものを処理したくない場合は、かなりスマートなソリューションです。私はそれが好きです!
M'sieur Toph '10

トランスクルージョンコンテンツは(テキストだけでなく)HTMLノードでなければならないことに注意してください
M'sieur Toph '10

1

私の場合、空のng-contentの親を非表示にする必要があります。

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

単純なcssは機能します:

.wrapper {
  display: inline-block;

  &:empty {
    display: none;
  }
}

1

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

完全な例 https //stackblitz.com/edit/angular-jjjdqb

私は、このソリューションが角度コンポーネントに実装されていることを発見しましmatSuffixた。フォームフィールドコンポーネントの。

成分の含有量が、後に注入された状況で、後にアプリが初期化され、我々はまたに加入することで、反応性の実装を使用することができるchangesのイベントQueryList

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();
  }

}

完全な例https//stackblitz.com/edit/angular-essvnq


1

Angular 10では、わずかに変更されています。使用するもの:

<div #ref><ng-content></ng-content></div> 
<span *ngIf="ref.children.length == 0">
  Display this if ng-content is empty!
</span>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.