Supongamos que tengo un componente:
@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 {
}
Ahora, me gustaría mostrar contenido predeterminado si <ng-content>
este componente está vacío. ¿Existe una manera fácil de hacer esto sin acceder directamente al DOM?
javascript
angular
Slawomir Dadas
fuente
fuente
Respuestas:
Envuelva
ng-content
un elemento HTML como adiv
para obtener una referencia local, luego vincule langIf
expresión aref.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>`
fuente
ref.children.length
. childNodes contendrátext
nodos si formatea su html con espacios o nuevas líneas, pero los niños aún estarán vacíos.ref.childNodes.length == 0
lugar deref.children.length == 0
. Sería de gran ayuda si pudieras editar la respuesta para que sea coherente. Fácil error, no golpearte. :)!ref.hasChildNodes()
lugar deref.nativeElement.childNodes.length == 0
Hay algunos que faltan en la respuesta de @pixelbits. Necesitamos verificar no solo la
children
propiedad, porque cualquier salto de línea o espacio en la plantilla principal provocará unchildren
elemento con texto en blanco \ saltos de línea . Mejor comprobar.innerHTML
y.trim()
él.Ejemplo de trabajo:
<span #ref><ng-content></ng-content></span> <span *ngIf="!ref.innerHTML.trim()"> Content if empty </span>
fuente
element.children.length
oelement.childnodes.length
, según lo que quieras pedir.EDITAR 17.03.2020
Pure CSS (2 soluciones)
Proporciona contenido predeterminado si no se proyecta nada en ng-content.
Posibles selectores:
:only-child
selector. Vea esta publicación aquí: Selector de hijo únicoEste requiere menos código / marcado. Soporte desde IE 9: ¿Puedo usar: hijo único?
:empty
selector. Solo sigue leyendo.Soporte de IE 9 y parcialmente desde 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; }
En caso de que no funcione
Tenga en cuenta que tener al menos un espacio en blanco se considera que no está vacío. Angular elimina los espacios en blanco, pero por si acaso no es así:
<div class="wrapper"><!-- --><ng-content select="my-component"></ng-content><!-- --></div>
o
<div class="wrapper"><ng-content select="my-component"></ng-content</div>
fuente
empty
pseudoclase css<loader [data]="allDataWeNeed"> <desired-component *loadingRender><desired-component> <other-default-component *renderWhenEmptyResult></other-default-component> </loader>
y en el componente de carga, podría mostrar la carga de rendimiento percibida, mientras los datos se están cargando. Puedes escribirlo / resolverlo de diferentes formas. Esta es una demostración de cómo podría resolverlo.Cuando inyecte el contenido agregue una variable de referencia:
y en su clase de componente obtenga una referencia al contenido inyectado con @ContentChild ()
@ContentChild('content') content: ElementRef;
por lo que en su plantilla de componente puede verificar si la variable de contenido tiene un valor
<div> <ng-content></ng-content> <span *ngIf="!content"> Display this if ng-content is empty! </span> </div>
fuente
<MyContainer></MyContainer>
. Su solución de espera a los usuarios crear un sub-elemento de bajo MyContainer:<MyContainer><div #content></div></MyContainer>
. Si bien esta es una posibilidad, no diría que esto sea superior.Inyecte
elementRef: ElementRef
y compruebe sielementRef.nativeElement
tiene hijos. Es posible que esto solo funcione conencapsulation: ViewEncapsulation.Native
.Envuelva la
<ng-content>
etiqueta y verifique si tiene hijos. Esto no funciona conencapsulation: ViewEncapsulation.Native
.<div #contentWrapper> <ng-content></ng-content> </div>
y comprueba si tiene hijos
@ViewChild('contentWrapper') contentWrapper; ngAfterViewInit() { contentWrapper.nativeElement.childNodes... }
(no probado)
fuente
@ViewChild
. Si bien es "Legal", ViewChild no debe usarse para acceder a los nodos secundarios dentro de una plantilla. Esto es un antipatrón porque, aunque los padres pueden saber acerca de los niños, no deben emparejarse con ellos, ya que generalmente conduce a un acoplamiento patológico ; Una forma más apropiada de transporte de datos o manejo de solicitudes es utilizar metodologías basadas en eventos .@ViewChild
. Gracias por agregar algo de equilibrio a mis comentarios. Encuentro nuestros dos comentarios tan dignos de consideración como el otro.@ViewChild()
proviene del nombre. Los "niños" no son necesariamente niños, son solo la parte declarativa del componente (como yo lo veo). Si las instancias de componentes creadas para este marcado son accesos, esto es, por supuesto, una historia diferente, porque requeriría conocimiento sobre al menos su interfaz pública y esto en realidad crearía un acoplamiento estrecho. Ésta es una conversación muy interesante. Me preocupa que no tenga tiempo suficiente para pensar en estos asuntos porque con tales marcos a menudo se quema el tiempo para encontrar alguna manera.Si desea mostrar un contenido predeterminado, ¿por qué no usa el selector 'hijo único' de css?
https://www.w3schools.com/cssref/sel_only-child.asp
por ejemplo: HTML
<div> <ng-content></ng-content> <div class="default-content">I am deafult</div> </div>
css
.default-content:not(:only-child) { display: none; }
fuente
Con Angular 10, ha cambiado ligeramente. Usarías:
<div #ref><ng-content></ng-content></div> <span *ngIf="ref.children.length == 0"> Display this if ng-content is empty! </span>
fuente
En mi caso, tengo que ocultar el padre del contenido ng vacío:
<span class="ml-1 wrapper"> <ng-content> </ng-content> </span>
CSS simple funciona:
.wrapper { display: inline-block; &:empty { display: none; } }
fuente
Implementé una solución usando el decorador @ContentChildren , que de alguna manera es similar a la respuesta de @ Lerner.
Según los documentos , este decorador:
Entonces, el código necesario en el componente principal será:
<app-my-component> <div #myComponentContent> This is my component content </div> </app-my-component>
En la clase de componente:
@ContentChildren('myComponentContent') content: QueryList<ElementRef>;
Luego, en la plantilla de componentes:
<div class="container"> <ng-content></ng-content> <span *ngIf="*ngIf="!content.length""><em>Display this if ng-content is empty!</em></span> </div>
Ejemplo completo : https://stackblitz.com/edit/angular-jjjdqb
Encontré esta solución implementada en componentes angulares, para
matSuffix
, en el componente de campo de formulario .En la situación en la que el contenido del componente se inyecta más tarde, después de inicializar la aplicación, también podemos usar una implementación reactiva, suscribiéndonos al
changes
evento deQueryList
: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(); } }
Ejemplo completo : https://stackblitz.com/edit/angular-essvnq
fuente