Angular 2'de <ng-içerik> boş olup olmadığı nasıl kontrol edilir?


92

Bir bileşenim olduğunu varsayalım:

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

Şimdi, <ng-content>bu bileşen için boşsa bazı varsayılan içeriği görüntülemek istiyorum . DOM'a doğrudan erişmeden bunu yapmanın kolay bir yolu var mı?



Bilginize, kabul edilen cevabın işe yaradığını biliyorum, ancak bileşenlere "useDefault" türü bir girdi parametresini varsayılan olarak false olarak ayarlamanın daha iyi bir stil olduğunu düşünüyorum.
bryan60

Yanıtlar:


97

Yerel bir referans almak için a ng-contentgibi bir HTML öğesini sarın div, ardından ngIfifadeyi şuna bağlayın 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>`

24
Bunun alternatif bir yolu yok mu? Aurelia yedek yuvalarına kıyasla bu çirkin olduğu için
Astronaut

18
Kullanması daha güvenlidir ref.children.length. html'nizi textboşluklarla veya yeni satırlarla biçimlendirirseniz, childNodes düğümler içerir , ancak çocuklar yine de boş kalır.
parlamento

5
Angular sorun izleyicide daha iyi bir yöntem için bir özellik isteği var: github.com/angular/angular/issues/12530 (oraya +1 eklemeye değer olabilir).
eppsilon

5
Bunun ref.childNodes.length == 0yerine örneğini kullandığımı fark edene kadar bunun işe yaramadığını bildirmek üzereydim ref.children.length == 0. Cevabı tutarlı olacak şekilde düzenleyebilirseniz bir grup yardımcı olabilirdi. Kolay hata, sana vurmamak. :)
Dustin Cleveland

3
Bu örneği takip ettim ve benim için iyi çalışıyor. Ama !ref.hasChildNodes()yerine kullandımref.nativeElement.childNodes.length == 0
Sergey Dzyuba

34

@Pixelbits yanıtında bazı eksikler var. childrenÜst şablondaki herhangi bir satır sonu veya boşluk childrenboş metin \ satır sonu içeren öğelere neden olacağı için sadece özelliği kontrol etmemiz gerekir . Kontrol etmek daha iyi .innerHTMLve .trim()onu.

Çalışma örneği:

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

1
benim için en iyi cevap :)
Kamil Kiełczewski

Bu yöntemi alt öğenin boş olup olmadığını kontrol etmek ve öyleyse üst öğeyi gizlemek için kullanabilir miyiz? Denedim, şans yok
Kavinda Jayakody

@KavindaJayakody Evet, bu çocuk elemanının boş olup olmadığını kontrol edecektir. Kodunuzu gösterin.
lfoma

* ngIf = "! ref.innerHTML.trim ()" Bu bölüm performans sorunlarına neden olacaktır. Trim, O (n).
RandomCode

1
@RandomCode doğru, işlev birden çok kez çağrılacak. Daha iyi bir yol, ne sormak istediğinize bağlı olarak element.children.length veya kontrol element.childnodes.lengthetmektir.
Stefan Rein

32

17.03.2020 DÜZENLEME

Saf CSS (2 çözüm)

Ng içeriğine hiçbir şey yansıtılmazsa varsayılan içeriği sağlar.

Olası seçiciler:

  1. :only-childseçici. Bu gönderiye buradan bakın : only-child Selector

    Bu, daha az kod / işaret gerektirir. IE 9'dan beri destek: Kullanabilir miyim: only-child

  2. :emptyseçici. Sadece daha fazlasını okuyun.

    IE 9'dan ve kısmen IE 7 / 8'den beri destek: 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;
}

Çalışmaması durumunda

En az bir boşluğa sahip olmanın boş olmadığı kabul edildiğini unutmayın. Angular, beyaz boşluğu kaldırır, ancak tam olarak değilse:

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

veya

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

1
Şimdiye kadar bu, kullanım durumum için en iyi çözümdü. Bir emptycss sahte
sınıfımız

@Stefan varsayılan içerik yine de işlenecek ... sadece gizlenecek. Dolayısıyla, karmaşık varsayılan içerik için bu en iyi çözüm değildir. Bu doğru mu?
BossOz

1
@BossOz Haklısın. Oluşturulacak (yapıcı vb. Açısal bir bileşeniniz varsa çağrılacaktır). Bu çözüm yalnızca aptal görünümler için işe yarar. Yüklemeyi veya bunun gibi şeyleri içeren karmaşık bir mantığınız varsa, bence en iyi bahsiniz, bir şablon / sınıf alabilen (bunu nasıl uygulamak isterseniz) ve mantığa bağlı olarak yapısal bir yönerge yazmaktır. istenen bileşen veya varsayılan olan. Yorum bölümü bir örnek için çok küçük. Uygulamalarımızdan birinde sahip olduğumuz başka bir örnek için tekrar yorum yapıyorum.
Stefan Rein

Bunun bir yolu, bir Direktif aracılığıyla ContentChildren'ı seçen bir bileşene sahip olmaktır (DirectiveClass ile sorgu, ancak yapısal yönerge olarak kullanım, bu nedenle yalnızca işaretlemeye sahip olmakla ilk önyükleme yapılmaz): <loader [data]="allDataWeNeed"> <desired-component *loadingRender><desired-component> <other-default-component *renderWhenEmptyResult></other-default-component> </loader>ve yükleme bileşeninde, algılanan performans yüklemesini gösterebilirsiniz. veriler yükleniyor. Bunu farklı şekillerde yazabilir / çözebilirsiniz. Bu, nasıl çözebileceğinizin bir göstergesidir.
Stefan Rein

21

İçeriği enjekte ettiğinizde bir referans değişkeni ekleyin:

<div #content>Some Content</div>

ve bileşen sınıfınızda @ContentChild () ile enjekte edilen içeriğe bir referans alın

@ContentChild('content') content: ElementRef;

böylece bileşen şablonunuzda içerik değişkeninin bir değeri olup olmadığını kontrol edebilirsiniz

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

Bu en temiz ve çok daha iyi cevap. Kutudan çıkar çıkmaz ContentChild API'yi destekler. En iyi cevabın neden bu kadar çok oy aldığını anlayamıyorum.
CarbonDry

10
OP, ngContent'in boş olup olmadığını sordu - bunun anlamı <MyContainer></MyContainer>. Çözümün kullanıcıları MyContainer altında bir alt öğe oluşturmak bekler: <MyContainer><div #content></div></MyContainer>. Bu bir olasılık olsa da, bunun daha üstün olduğunu söyleyemem.
pixelbits

8

Enjekte edin elementRef: ElementRefve elementRef.nativeElementçocuk olup olmadığını kontrol edin . Bu sadece işe yarayabilir encapsulation: ViewEncapsulation.Native.

<ng-content>Etiketi sarın ve alt öğesi olup olmadığını kontrol edin. Bu işe yaramıyor encapsulation: ViewEncapsulation.Native.

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

ve çocuğu olup olmadığını kontrol edin

@ViewChild('contentWrapper') contentWrapper;

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

(test edilmedi)


3
Kullanımı nedeniyle bunu reddettim @ViewChild. "Yasal" olsa da ViewChild, bir şablon içindeki alt düğümlere erişmek için kullanılmamalıdır. Ebeveynler olabilir iken Bu bir antipattern çünkü biliyorum çocuklar hakkında, gerektiği değil Çift için genellikle potansiyel müşteriler gibi onlara Patolojik Coupling ; daha uygun bir veri taşıma veya talep işleme biçimi, olay güdümlü metodolojileri kullanmaktır .
Cody

5
@Cody, olumsuz oyunuza yorum gönderdiğiniz için teşekkürler. Aslında argümanınızı takip etmiyorum. Şablon ve bileşenler sınıfı tek bir birimdir - bir bileşendir. Şablona koddan erişmenin neden bir anti-model olması gerektiğini anlamıyorum. Angular (2/4) 'ün AngularJS ile neredeyse hiçbir ortak yanı yoktur, ancak her ikisi de web çerçeveleri ve adıdır.
Günter Zöchbauer

2
Gunter, gerçekten iyi iki noktaya değindin. Angular, AngularJS'nin yollarından bir stigma ile mutlaka çıkmamalıdır. Ancak, ilk noktanın (ve yukarıda değindiklerimin) felsefi mühendislik boşluğuna düştüğünü belirtmek isterim. Bununla birlikte, Yazılım Birleştirme konusunda bir uzman haline geldim ve [en azından ağır] kullanımına karşı tavsiyede bulunuyorum @ViewChild. Yorumlarıma biraz denge kattığınız için teşekkürler - Her iki yorumu da diğeri kadar dikkate almaya değer buluyorum.
Cody

2
@Cody belki de güçlü fikriniz @ViewChild()isminden geliyor. "Çocuklar" mutlaka çocuk değildir, onlar sadece bileşenin bildirimsel parçasıdır (gördüğüm kadarıyla). Bu işaretleme için oluşturulan bileşen örnekleri erişimlerse, bu elbette farklı bir hikaye, çünkü en azından genel arayüzleri hakkında bilgi gerektirecek ve bu aslında sıkı bir bağlantı yaratacaktır. Bu çok ilginç bir tartışma. Bu tür konularda düşünmek için yeterli zamanım olmaması beni endişelendiriyor çünkü bu tür çerçevelerde çoğu zaman herhangi bir yol bulmak için zaman harcanıyor.
Günter Zöchbauer

3
Bunu API'de açığa
vurmamak

5

Varsayılan bir içeriği görüntülemek istiyorsanız, neden css'deki 'tek çocuk' seçiciyi kullanmıyorsunuz?

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

örneğin: HTML

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

css

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

ViewChild ve Bileşen içindeki malzemelerle uğraşmak istemiyorsanız, oldukça akıllı bir çözüm. Bunu sevdim!
M'sieur Toph '

Lütfen transclude içeriğinin bir HTML düğümü olması gerektiğine dikkat edin (yalnızca metin değil)
M'sieur Toph '

2

Angular 10 ile biraz değişti. Kullanacaksın:

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

1

Benim durumumda boş ng içeriğinin üst öğesini gizlemeliyim:

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

Basit css çalışır:

.wrapper {
  display: inline-block;

  &:empty {
    display: none;
  }
}

1

@ContentChildren dekoratörünü kullanarak bir çözüm uyguladım , bu bir şekilde @ Lerner'ın cevabına benzer.

Belgelere göre , bu dekoratör:

İçerik DOM'dan öğeler veya yönergelerin QueryList'ini alın. Bir alt öğe eklendiğinde, kaldırıldığında veya taşındığında, sorgu listesi güncellenecek ve sorgu listesindeki gözlemlenebilir değişiklikler yeni bir değer yayınlayacaktır.

Dolayısıyla, üst bileşendeki gerekli kod şöyle olacaktır:

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

Bileşen sınıfında:

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

Ardından, bileşen şablonunda:

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

Tam örnek : https://stackblitz.com/edit/angular-jjjdqb

Bu çözümü matSuffix, form alanı bileşeni için açısal bileşenlerde uygulanmış buldum .

Bileşenin içeriği daha sonra enjekte edilen durumda, sonra uygulama başlatıldığını, biz de abone olarak, reaktif bir uygulama kullanabilirsiniz changesdurumunda 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();
  }

}

Tam örnek : https://stackblitz.com/edit/angular-essvnq

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.