Допустим, у меня есть компонент:
@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?
javascript
angular
Славомир Дадас
источник
источник
Ответы:
Оберните
ng-content
элемент HTML, например a,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>`
источник
ref.children.length
. childNodes будет содержатьtext
узлы, если вы отформатируете свой html пробелами или новыми строками, но дочерние элементы все равно останутся пустыми.ref.childNodes.length == 0
вместо вместоref.children.length == 0
. Было бы здорово, если бы вы могли отредактировать ответ, чтобы он был последовательным. Легкая ошибка, не задевающая вас. :)!ref.hasChildNodes()
вместоref.nativeElement.childNodes.length == 0
В ответе @pixelbits некоторые отсутствуют. Нам нужно проверять не только
children
свойство, потому что любые разрывы строк или пробелы в родительском шаблоне вызовутchildren
элемент с пустым текстом \ разрывы строк . Лучше проверить.innerHTML
и.trim()
это.Рабочий пример:
<span #ref><ng-content></ng-content></span> <span *ngIf="!ref.innerHTML.trim()"> Content if empty </span>
источник
element.children.length
илиelement.childnodes.length
, в зависимости от того, что вы хотите спросить.РЕДАКТИРОВАТЬ 17.03.2020
Чистый CSS (2 решения)
Предоставляет контент по умолчанию, если в ng-content ничего не проецируется.
Возможные селекторы:
:only-child
селектор. См. Этот пост здесь :: only-child SelectorЭто требует меньше кода / разметки. Поддержка с IE 9: Могу ли я использовать: only-child
:empty
селектор. Просто читайте дальше.Поддержка IE 9 и частично с 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; }
Если это не работает
Имейте в виду, что наличие хотя бы одного пробела не считается пустым. Angular удаляет пробелы, но на всякий случай, если это не так:
<div class="wrapper"><!-- --><ng-content select="my-component"></ng-content><!-- --></div>
или
<div class="wrapper"><ng-content select="my-component"></ng-content</div>
источник
empty
псевдокласс css<loader [data]="allDataWeNeed"> <desired-component *loadingRender><desired-component> <other-default-component *renderWhenEmptyResult></other-default-component> </loader>
загрузка не связана только с разметкой): и в компоненте загрузки вы можете показать воспринимаемую загрузку производительности, в то время как данные загружаются. Писать / решать можно по-разному. Это одна из демонстраций того, как вы могли бы решить эту проблему.Когда вы вводите контент, добавьте ссылочную переменную:
и в вашем классе компонентов получите ссылку на внедренный контент с помощью @ContentChild ()
@ContentChild('content') content: ElementRef;
поэтому в шаблоне компонента вы можете проверить, имеет ли переменная содержимого значение
<div> <ng-content></ng-content> <span *ngIf="!content"> Display this if ng-content is empty! </span> </div>
источник
<MyContainer></MyContainer>
. Ваше решение ожидает пользователям создавать подэлемент под MyContainer:<MyContainer><div #content></div></MyContainer>
. Хотя это возможно, я бы не сказал, что это лучше.Сделайте инъекцию
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... }
(не испытано)
источник
@ViewChild
. Будучи «легальным», ViewChild не следует использовать для доступа к дочерним узлам в шаблоне. Это антипаттерн, потому что, хотя родители могут знать о детях, они не должны объединяться с ними, поскольку это обычно приводит к патологическому сцеплению ; более подходящей формой передачи данных или обработки запросов является использование методологий, управляемых событиями .@ViewChild
. Спасибо за добавление баланса в мои комментарии - я считаю оба наших комментария столь же достойными внимания, как и другой.@ViewChild()
исходит из названия. «Дочерние элементы» не обязательно являются дочерними, это просто декларативная часть компонента (как я это вижу). Если экземпляры компонентов, созданные для этой разметки, являются доступами, это, конечно, другая история, потому что для этого потребуются знания, по крайней мере, об их общедоступном интерфейсе, и это фактически создаст тесную связь. Это очень интересное обсуждение. Меня беспокоит, что у меня нет достаточно времени, чтобы подумать о таких вещах, потому что с такими фреймворками слишком часто время тратится, чтобы найти какой-либо способ вообще.Если вы хотите отображать контент по умолчанию, почему бы вам просто не использовать селектор 'only-child' из css.
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; }
источник
В Angular 10 он немного изменился. Вы бы использовали:
<div #ref><ng-content></ng-content></div> <span *ngIf="ref.children.length == 0"> Display this if ng-content is empty! </span>
источник
В моем случае мне нужно скрыть родителя пустого ng-content:
<span class="ml-1 wrapper"> <ng-content> </ng-content> </span>
Простой css работает:
.wrapper { display: inline-block; &:empty { display: none; } }
источник
Я реализовал решение, используя @ContentChildren декоратор , который чем-то похож на ответ .
Согласно документам , этот декоратор:
Итак, необходимый код в родительском компоненте будет:
<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
источник