Tengo un componente secundario que acepta un TemplateRef como @Input y lo muestra a través ngTemplateOutlet . ¿Cómo puedo usar @ViewChild / @ViewChildren para recuperar componentes dentro de la plantilla?
Si la plantilla se declara en el mismo componente en el que se usa, @ViewChild / @ViewChildren están funcionando pero hacen que el componente sea mucho menos dinámico.
También intenté usar @ContentChildren pero no hace ninguna diferencia.
Creé un stackblitz para reproducir. Estos son algunos de los códigos:
<ng-container #fromParent [ngTemplateOutlet]="template"></ng-container> @Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.css'], }) export class ChildComponent implements AfterViewInit { @Input() template: TemplateRef<unknown>; @ViewChildren(HelloComponent) hello = new QueryList<HelloComponent>(); ngAfterViewInit() { console.log('Has hello', this.hello.length > 0); } } <ng-template #tmp> <hello name="{{ name }}"></hello> </ng-template> <app-child [template]="tmp"> </app-child> El componente secundario de inicio de sesión devuelve false .
Aquí está el stackblitz: https://stackblitz.com/edit/angular-eopzyw?file=src/app/app.component.ts
Gracias.
Esto es interesante, a primera vista, yo también pensé que debería ser accesible, pero hice una depuración y encontré que:
La plantilla principal es parte de la vista de host del componente principal y, por lo tanto, ViewChildren en el componente secundario no podrá acceder a ella, ya que no es parte de la vista de host del componente secundario.
Mire la imagen a continuación, el componente de la aplicación tiene una identificación de host 165 y la plantilla ng principal se asocia con él.
Entonces parece que en el diseño actual de ViewChildren no admite consultar las plantillas pasadas desde una vista de host. Puede ser una buena idea abrir una solicitud de función.
Según lo que descubrió @Ritesh Waghela, encontré una manera de obtener las vistas dentro de la plantilla, pero tiene algunos inconvenientes.
Entonces, debido a que solo el padre conoce la existencia de esas vistas, es su trabajo obtener sus referencias.
@ViewChildren(HelloComponent) public helloComponents = new QueryList<HelloComponent>(); El niño obtendrá una referencia al padre gracias a Injector .
private parent: ParentComponent; constructor(private injector: Injector) { this.parent = injector.get<ParentComponent>(ParentComponent); } Debido a que @ViewChildren del padre tomará todo el HelloComponent , en plantilla o no, necesitamos diferenciarlos. Para esto no encontré mejor solución que agregar un atributo dentro HelloComponent .
@Input() fromTemplate = false; El componente principal puede distinguir HelloComponent dentro de la plantilla o no:
<ng-template #tmp> <hello name="inside template" fromTemplate="true"></hello> </ng-template> <hello name="outside template"></hello> <app-child [template]="tmp"> </app-child>El componente secundario ahora puede filtrar las vistas de los padres para obtener lo que está buscando.
ngAfterViewInit() { const viewFromTemplate = this.parent.helloComponents.filter( (comp) => comp.fromTemplate ); }Esta solución funciona, pero tiene algunos inconvenientes:
El primer inconveniente no es un gran problema en mi caso (se puede solucionar porque todos mis componentes están contenidos dentro de un envoltorio -> Tomo la referencia al envoltorio conocido que puede obtener una referencia al componente que aloja).
Pero el segundo inconveniente me parece más problemático. ¿Alguien sabe si una vista puede saber si es parte de una ng-template o no? Acepto cualquier sugerencia al respecto.
Creé un stackblitz para demostrar mi solución: https://angular-qgwcdf.stackblitz.io .