कोणीय 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 को सीधे एक्सेस किए बिना ऐसा करने का एक आसान तरीका है?



FYI करें, मैं स्वीकृत उत्तर कार्यों को जानता हूं, लेकिन मुझे लगता है कि घटकों के लिए एक "यूज़डिफॉल्ट" प्रकार के इनपुट पैरामीटर को पास करना बेहतर है, जो कि डिफ़ॉल्ट रूप से गलत है।
bryan60

जवाबों:


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
क्या इसका कोई वैकल्पिक तरीका नहीं है? क्योंकि यह बदसूरत है,
एस्ट्रोनॉट

18
यह उपयोग करने के लिए सुरक्षित है ref.children.lengthtextयदि आप अपने HTML को रिक्त स्थान या नई लाइनों के साथ स्वरूपित करते हैं, तो बच्चे नोड्स होंगे , लेकिन बच्चे अभी भी खाली होंगे।
संसद

5
एंगुलर इश्यू ट्रैकर पर एक बेहतर विधि के लिए एक सुविधा अनुरोध है: github.com/angular/angular/issues/12530 (वहां +1 जोड़ने लायक हो सकता है)।
एप्सिलॉन

5
मैं पोस्ट करने वाला था कि यह तब तक काम नहीं करता था जब तक मुझे एहसास नहीं हुआ कि मैंने ref.childNodes.length == 0इसके बजाय उदाहरण का उपयोग किया है ref.children.length == 0। यदि आप उत्तर को सुसंगत बनाने के लिए संपादित कर सकते हैं तो यह एक गुच्छा में मदद करेगा। आसान गलती, आपको कोसना नहीं। :)
डस्टिन क्लीवलैंड

3
मैंने इस उदाहरण का पालन किया और यह मेरे लिए ठीक है। लेकिन मैंने !ref.hasChildNodes()इसके बजाय इस्तेमाल कियाref.nativeElement.childNodes.length == 0
सर्गेई डेज़ुबा

34

वहाँ कुछ @pixelbits जवाब में गायब है। हमें न केवल childrenसंपत्ति की जांच करने की आवश्यकता है , क्योंकि माता-पिता के टेम्पलेट में कोई भी रेखा टूट जाती है या रिक्त स्थान childrenखाली टेक्स्ट \ linebreaks के साथ तत्व का कारण होगा । जांच करने के लिए बेहतर है .innerHTMLऔर .trim()यह।

काम करने का उदाहरण:

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

1
मेरे लिए सबसे अच्छा जवाब :)
कामिल केवल्स्कीस्की

क्या हम इस पद्धति का उपयोग यह जांचने के लिए कर सकते हैं कि क्या बाल तत्व खाली है और यदि माता-पिता छिपते हैं तो? मैंने कोशिश की, कोई किस्मत नहीं
काव्या जयकॉदी

@KavindaJayakody हाँ, यह खाली के लिए बाल तत्व की जाँच करेगा। अपना कोड दिखाएं।
लेफोमा

* ngIf = "! ref.innerHTML.trim ()" यह भाग प्रदर्शन समस्याओं का कारण बनेगा। ट्रिम O (n) है।
रैंडमकोड

1
@RandomCode सही है, फ़ंक्शन को कई बार कहा जाएगा। एक बेहतर तरीका जाँच करने के लिए है element.children.length या element.childnodes.length, क्या आप के लिए पूछना चाहता हूँ पर निर्भर करता है।
स्टीफन रीन

31

EDIT 17.03.2020

शुद्ध सीएसएस (2 समाधान)

यदि कुछ भी गैर-सामग्री में अनुमानित नहीं है तो डिफ़ॉल्ट सामग्री प्रदान करता है।

संभावित चयनकर्ता:

  1. :only-childचयनकर्ता। इस पोस्ट को यहाँ देखें : केवल-चाइल्ड सेलेक्टर

    इसके लिए कम कोड / मार्कअप की आवश्यकता होती है। IE 9 के बाद से समर्थन: क्या मैं उपयोग कर सकता हूं: केवल-बच्चा

  2. :emptyचयनकर्ता। अभी आगे पढ़िए।

    IE 7/8 के बाद से IE 9 और आंशिक रूप से समर्थन: 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>

सीएसएस

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

मामले में यह काम नहीं कर रहा है

के बारे में पता है, कि कम से कम एक व्हाट्सएप खाली नहीं होने के लिए माना जाता है। कोणीय व्हाट्सएप को हटा देता है, लेकिन अगर ऐसा नहीं है तो:

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

या

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

1
अब तक यह मेरे उपयोग के मामले का सबसे अच्छा समाधान था। मुझे याद नहीं था कि हमारे पास empty
julianobrasil

@ डिफ़ॉल्ट सामग्री वैसे भी प्रदान की जाएगी ... यह केवल छिपाया जाएगा। तो, जटिल डिफ़ॉल्ट सामग्री के लिए यह सबसे अच्छा समाधान नहीं है। क्या यह सही है?
बॉसओज

1
@BossOz आप सही हैं। इसका प्रतिपादन किया जाएगा (निर्माणकर्ता कहा जाएगा यदि आपके पास एक कोणीय घटक है)। यह समाधान केवल गूंगे विचारों के लिए अच्छा काम करता है। यदि आपके पास जटिल तर्क है, जैसे लोडिंग या सामान शामिल है, तो मेरी राय में आपकी सबसे अच्छी शर्त एक संरचनात्मक निर्देश लिखना है, जो एक टेम्पलेट / वर्ग प्राप्त कर सकता है (हालांकि आप इसे लागू करना चाहते हैं) और तर्क के आधार पर, फिर रेंडर करें वांछित घटक या डिफ़ॉल्ट एक। एक उदाहरण के लिए टिप्पणी अनुभाग बहुत छोटा है। मैं फिर से एक उदाहरण के लिए टिप्पणी करता हूं जो हमारे पास हमारे एक ऐप में है।
स्टीफन रीन

एक तरह से एक घटक हो सकता है जिसमें एक निर्देश के माध्यम से ContentChildren (DirectiveClass के साथ क्वेरी, लेकिन संरचनात्मक निर्देश के रूप में उपयोग होता है, इसलिए केवल मार्कअप के साथ कोई प्रारंभिक बूटस्ट्रैपिंग शामिल नहीं है): <loader [data]="allDataWeNeed"> <desired-component *loadingRender><desired-component> <other-default-component *renderWhenEmptyResult></other-default-component> </loader>और लोडिंग घटक में, आप कथित प्रदर्शन लोडिंग दिखा सकते हैं, जबकि डेटा लोड हो रहा है। आप इसे विभिन्न तरीकों से लिख / हल कर सकते हैं। यह एक प्रदर्शन है कि आप इसे कैसे हल कर सकते हैं।
स्टीफन रीन

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> 

यह सबसे साफ और बेहतर जवाब है। यह बॉक्स से ContentChild API को सपोर्ट करता है। समझ में नहीं आता कि शीर्ष उत्तर को इतने वोट क्यों मिले।
कार्बनड्री

10
ओपी ने पूछा है कि क्या नहीं या गैरकानूनी खाली है - इसका मतलब है <MyContainer></MyContainer>। आपका समाधान उपयोगकर्ताओं को MyContainer के तहत एक उप-तत्व बनाने की उम्मीद करता है <MyContainer><div #content></div></MyContainer>:। जबकि यह एक संभावना है, मैं यह नहीं कहूंगा कि यह श्रेष्ठ है।
2

8

किसी भी बच्चे को इंजेक्शन 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...
}

(टेस्ट नहीं हुआ)


3
के उपयोग के कारण मैंने इसे कम कर दिया @ViewChild। जबकि "कानूनी", ViewChild एक टेम्पलेट के भीतर बच्चे नोड्स तक पहुँचने के लिए इस्तेमाल नहीं किया जाना चाहिए। यह एक एंटीपैटर्न है, क्योंकि माता-पिता बच्चों के बारे में जान सकते हैं , उन्हें युगल नहीं करना चाहिए क्योंकि यह आमतौर पर पैथोलॉजिकल युग्मन की ओर जाता है ; डेटा-ट्रांसपोर्ट या अनुरोध-हैंडलिंग का एक और अधिक उपयुक्त रूप इवेंट-संचालित कार्यप्रणाली का उपयोग करना है
कोडी

5
@ अपने डाउनवोट पर टिप्पणी पोस्ट करने के लिए धन्यवाद। वास्तव में मैं आपके तर्क का पालन नहीं करता। टेम्पलेट और घटक वर्ग एक एकल इकाई हैं - एक घटक। मैं यह नहीं देखता कि कोड से टेम्प्लेट एक्सेस करना एक एंटीपैटर्न क्यों होना चाहिए। एंगुलर (2/4) में एंगुलरजेएस के साथ लगभग कुछ भी नहीं है, सिवाय इसके कि दोनों वेब फ्रेमवर्क और नाम हैं।
गुंटर Zöchbauer

2
गंटर, आप दो बहुत अच्छे अंक बनाते हैं। कोणीय को आवश्यक रूप से एंगुलरजेएस के तरीकों से एक कलंक के साथ हैच नहीं करना चाहिए। लेकिन मुझे लगता है कि पहला बिंदु (और जो मैंने ऊपर बनाया है) इंजीनियरिंग के दार्शनिक नाली की ओर गिरता है। उस ने कहा, मैं सॉफ्टवेयर युग्मन पर एक विशेषज्ञ के बारे में कुछ बन गया हूं और मैं [कम से कम भारी] उपयोग के खिलाफ सलाह दूंगा @ViewChild। मेरी टिप्पणियों में कुछ संतुलन जोड़ने के लिए धन्यवाद - मुझे हमारी दोनों टिप्पणियाँ अन्य के रूप में विचार के योग्य हैं।
कोड़ी

2
@ कोडी शायद @ViewChild()नाम के बारे में आपकी मजबूत राय है । "बच्चे" आवश्यक रूप से बच्चे नहीं हैं, वे सिर्फ घटक का घोषित हिस्सा हैं (जैसा कि मैं इसे देखता हूं)। यदि इस मार्कअप के लिए बनाए गए घटक उदाहरण एक्सेस हैं, तो बेशक यह एक अलग कहानी है, क्योंकि इसके लिए कम से कम उनके सार्वजनिक इंटरफ़ेस के बारे में ज्ञान की आवश्यकता होगी और यह वास्तव में तंग युग्मन पैदा करेगा। यह बहुत दिलचस्प चर्चा है। यह मुझे चिंतित करता है कि मेरे पास इस तरह के मामलों के बारे में सोचने के लिए पर्याप्त समय नहीं है क्योंकि ऐसे ढांचे के साथ अक्सर किसी भी तरह से खोजने के लिए समय जला दिया जाता है।
गुंटर ज़ोचबॉउर

3
एपीआई में इसे उजागर नहीं करना वास्तविक विरोधी पैटर्न है :-(
सिमोन_विजय

4

यदि आप डिफॉल्ट कंटेंट प्रदर्शित करना चाहते हैं तो न केवल आप सीएसएस से 'एकमात्र-बच्चे' चयनकर्ता का उपयोग करें।

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

उदाहरण के लिए: HTML

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

सीएसएस

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

बल्कि स्मार्ट समाधान, यदि आप घटक में ViewChild और सामान के साथ सौदा नहीं करना चाहते हैं। मुझें यह पसंद है!
21

कृपया ध्यान दें कि ट्रांस्क्लूड कंटेंट का HTML नोड होना चाहिए (केवल टेक्स्ट नहीं)
Mieur Toph '

1

मेरे मामले में मुझे खाली एनजी सामग्री के माता-पिता को छिपाना होगा:

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

सरल सीएसएस काम करता है:

.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, में पाया है फॉर्म-फील्ड घटक में।

स्थिति जब घटक की सामग्री पर बाद में इंजेक्ट किया जाता है में, के बाद एप्लिकेशन initialised है, हम भी एक प्रतिक्रियाशील कार्यान्वयन का उपयोग कर सकते हैं, करने के लिए सदस्यता लेने के द्वारा 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

कोणीय 10 के साथ, यह थोड़ा बदल गया है। आप उपयोग करेंगे:

<div #ref><ng-content></ng-content></div> 
<span *ngIf="ref.children.length == 0">
  Display this if ng-content is empty!
</span>
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.