Załóżmy, że mam komponent:
@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 {
}
Teraz chciałbym wyświetlić domyślną zawartość, jeśli <ng-content>
ten komponent jest pusty. Czy istnieje łatwy sposób na zrobienie tego bez bezpośredniego dostępu do DOM?
javascript
angular
Sławomir Dadas
źródło
źródło
Odpowiedzi:
Zawiń
ng-content
element HTML, taki jak a,div
aby uzyskać lokalne odwołanie do niego, a następnie powiążngIf
wyrażenie zref.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>`
źródło
ref.children.length
. childNodes będą zawieraćtext
węzły, jeśli sformatujesz swój kod HTML spacjami lub nowymi wierszami, ale elementy podrzędne nadal będą puste.ref.childNodes.length == 0
zamiastref.children.length == 0
. Pomogłoby wielu, gdybyś mógł edytować odpowiedź, aby była spójna. Łatwy błąd, żeby cię nie uderzyć. :)!ref.hasChildNodes()
zamiast tego użyłemref.nativeElement.childNodes.length == 0
Brakuje niektórych w odpowiedzi @pixelbits. Musimy sprawdzić nie tylko
children
właściwość, ponieważ wszelkie podziały wierszy lub spacje w szablonie nadrzędnym spowodują, żechildren
element z pustym tekstem \ podziałami wiersza. Lepiej to sprawdzić.innerHTML
i.trim()
to.Przykład pracy:
<span #ref><ng-content></ng-content></span> <span *ngIf="!ref.innerHTML.trim()"> Content if empty </span>
źródło
element.children.length
lubelement.childnodes.length
, w zależności od tego, o co chcesz zapytać.EDYCJA 17.03.2020
Pure CSS (2 rozwiązania)
Zapewnia domyślną zawartość, jeśli nic nie jest rzutowane na zawartość ng-content.
Możliwe selektory:
:only-child
selektor. Zobacz ten post tutaj :: only-child SelectorTen wymaga mniej kodu / znaczników. Wsparcie od IE 9: Can I Use: only-child
:empty
selektor. Przeczytaj dalej.Wsparcie z IE 9 i częściowo od 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; }
Na wypadek, gdyby to nie działało
Należy pamiętać, że posiadanie co najmniej jednej spacji jest uważane za niepuste. Angular usuwa spacje, ale na wszelki wypadek, jeśli tak nie jest:
<div class="wrapper"><!-- --><ng-content select="my-component"></ng-content><!-- --></div>
lub
<div class="wrapper"><ng-content select="my-component"></ng-content</div>
źródło
empty
pseudoklasę CSS<loader [data]="allDataWeNeed"> <desired-component *loadingRender><desired-component> <other-default-component *renderWhenEmptyResult></other-default-component> </loader>
aw komponencie ładującym można wyświetlić postrzegane ładowanie wydajności, podczas gdy dane się ładują. Możesz napisać / rozwiązać to na różne sposoby. To jest jedna demonstracja, jak możesz to rozwiązać.Po wstrzyknięciu zawartości dodaj zmienną odniesienia:
aw swojej klasie komponentu pobierz odwołanie do wstrzykniętej zawartości za pomocą @ContentChild ()
@ContentChild('content') content: ElementRef;
więc w szablonie komponentu możesz sprawdzić, czy zmienna zawartości ma wartość
<div> <ng-content></ng-content> <span *ngIf="!content"> Display this if ng-content is empty! </span> </div>
źródło
<MyContainer></MyContainer>
. Rozwiązanie przewiduje użytkownikom tworzenie sub-element pod MyContainer:<MyContainer><div #content></div></MyContainer>
. Chociaż jest to możliwość, nie powiedziałbym, że jest to lepsze.Wstrzyknij
elementRef: ElementRef
i sprawdź, czyelementRef.nativeElement
nie ma dzieci. To może działać tylko zencapsulation: ViewEncapsulation.Native
.Owiń
<ng-content>
tag i sprawdź, czy ma dzieci. To nie działa zencapsulation: ViewEncapsulation.Native
.<div #contentWrapper> <ng-content></ng-content> </div>
i sprawdź, czy ma dzieci
@ViewChild('contentWrapper') contentWrapper; ngAfterViewInit() { contentWrapper.nativeElement.childNodes... }
(nie testowany)
źródło
@ViewChild
. Chociaż „Legal”, ViewChild nie powinno być używane do uzyskiwania dostępu do węzłów podrzędnych w szablonie. Jest to antywzorzec projektowy dlatego, podczas gdy rodzice mogą wiedzieć o dzieciach, nie powinny one Para do nich, jak to zwykle prowadzi do patologicznej Coupling ; bardziej odpowiednią formą transportu danych lub obsługi wniosków jest wykorzystanie metodologii sterowanej zdarzeniami .@ViewChild
. Dziękuję za zrównoważenie moich komentarzy - uważam, że oba nasze komentarze są tak samo warte rozważenia, jak inne.@ViewChild()
pochodzi od nazwy. „Dzieci” niekoniecznie są dziećmi, są po prostu deklaratywną częścią komponentu (tak jak ja to widzę). Jeśli instancje komponentów utworzone dla tego znacznika są dostępami, jest to oczywiście inna historia, ponieważ wymagałoby to wiedzy przynajmniej o ich publicznym interfejsie, a to faktycznie stworzyłoby ścisłe powiązanie. To bardzo interesująca dyskusja. Martwi mnie, że nie mam czasu na myślenie o takich sprawach, ponieważ przy takich ramach zbyt często marnuje się czas, aby w ogóle znaleźć sposób.Jeśli chcesz wyświetlić domyślną zawartość, dlaczego nie, po prostu użyj selektora „only-child” z css.
https://www.w3schools.com/cssref/sel_only-child.asp
na przykład: HTML
<div> <ng-content></ng-content> <div class="default-content">I am deafult</div> </div>
css
.default-content:not(:only-child) { display: none; }
źródło
W Angular 10 nieco się to zmieniło. Użyłbyś:
<div #ref><ng-content></ng-content></div> <span *ngIf="ref.children.length == 0"> Display this if ng-content is empty! </span>
źródło
W moim przypadku muszę ukryć rodzica pustej zawartości ng:
<span class="ml-1 wrapper"> <ng-content> </ng-content> </span>
Proste działanie CSS:
.wrapper { display: inline-block; &:empty { display: none; } }
źródło
Wdrożyłem rozwiązanie za pomocą dekoratora @ContentChildren , które jest w pewnym sensie podobne do odpowiedzi @ Lernera.
Według dokumentów ten dekorator:
Zatem niezbędnym kodem w komponencie nadrzędnym będzie:
<app-my-component> <div #myComponentContent> This is my component content </div> </app-my-component>
W klasie komponentów:
@ContentChildren('myComponentContent') content: QueryList<ElementRef>;
Następnie w szablonie komponentów:
<div class="container"> <ng-content></ng-content> <span *ngIf="*ngIf="!content.length""><em>Display this if ng-content is empty!</em></span> </div>
Pełny przykład : https://stackblitz.com/edit/angular-jjjdqb
Znalazłem to rozwiązanie zaimplementowane w komponentach kątowych, dla
matSuffix
, w polu formularza komponencie .W sytuacji, gdy zawartość komponentu jest wstrzykiwana później, po zainicjowaniu aplikacji, możemy również skorzystać z implementacji reaktywnej, subskrybując
changes
zdarzenieQueryList
: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(); } }
Pełny przykład : https://stackblitz.com/edit/angular-essvnq
źródło