Estoy tratando de aprender Angular 2.
Me gustaría acceder a un componente secundario desde un componente primario utilizando la Anotación @ViewChild .
Aquí algunas líneas de código:
En BodyContent.ts tengo:
import {ViewChild, Component, Injectable} from 'angular2/core';
import {FilterTiles} from '../Components/FilterTiles/FilterTiles';
@Component({
selector: 'ico-body-content'
, templateUrl: 'App/Pages/Filters/BodyContent/BodyContent.html'
, directives: [FilterTiles]
})
export class BodyContent {
@ViewChild(FilterTiles) ft:FilterTiles;
public onClickSidebar(clickedElement: string) {
console.log(this.ft);
var startingFilter = {
title: 'cognomi',
values: [
'griffin'
, 'simpson'
]}
this.ft.tiles.push(startingFilter);
}
}
mientras que en FilterTiles.ts :
import {Component} from 'angular2/core';
@Component({
selector: 'ico-filter-tiles'
,templateUrl: 'App/Pages/Filters/Components/FilterTiles/FilterTiles.html'
})
export class FilterTiles {
public tiles = [];
public constructor(){};
}
Finalmente aquí las plantillas (como se sugiere en los comentarios):
BodyContent.html
<div (click)="onClickSidebar()" class="row" style="height:200px; background-color:red;">
<ico-filter-tiles></ico-filter-tiles>
</div>
FilterTiles.html
<h1>Tiles loaded</h1>
<div *ngFor="#tile of tiles" class="col-md-4">
... stuff ...
</div>
La plantilla FilterTiles.html está cargada correctamente en la etiqueta ico-filter-tiles (de hecho, puedo ver el encabezado).
Nota: la clase BodyContent se inyecta dentro de otra plantilla (Body) usando DynamicComponetLoader: dcl.loadAsRoot (BodyContent, '# ico-bodyContent', inyector):
import {ViewChild, Component, DynamicComponentLoader, Injector} from 'angular2/core';
import {Body} from '../../Layout/Dashboard/Body/Body';
import {BodyContent} from './BodyContent/BodyContent';
@Component({
selector: 'filters'
, templateUrl: 'App/Pages/Filters/Filters.html'
, directives: [Body, Sidebar, Navbar]
})
export class Filters {
constructor(dcl: DynamicComponentLoader, injector: Injector) {
dcl.loadAsRoot(BodyContent, '#ico-bodyContent', injector);
dcl.loadAsRoot(SidebarContent, '#ico-sidebarContent', injector);
}
}
El problema es que cuando trato de escribir ft
en el registro de la consola, obtengo undefined
, y por supuesto obtengo una excepción cuando intento insertar algo dentro de la matriz de "mosaicos": 'no hay mosaicos de propiedades para "indefinido"' .
Una cosa más: el componente FilterTiles parece estar cargado correctamente, ya que puedo ver la plantilla html para él.
¿Cualquier sugerencia? Gracias
fuente
ft
no se establecería en el constructor, pero en un controlador de eventos click ya estaría configurado.loadAsRoot
, que tiene un problema conocido con la detección de cambios. Solo para asegurarte de usarloadNextToLocation
oloadIntoLocation
.loadAsRoot
. Una vez que lo reemplacé conloadIntoLocation
el problema se resolvió. Si haces tu comentario como respuesta, puedo marcarlo como aceptadoRespuestas:
Tuve un problema similar y pensé en publicar en caso de que alguien cometiera el mismo error. Primero, una cosa a considerar es
AfterViewInit
; debe esperar a que se inicialice la vista antes de poder acceder a su@ViewChild
. Sin embargo, mi@ViewChild
todavía regresaba nulo. El problema fue mi*ngIf
. La*ngIf
directiva estaba matando mi componente de controles, así que no pude hacer referencia a él.Espero que ayude.
EDITAR
Como se menciona en @Ashg a continuación , una solución es usar en
@ViewChildren
lugar de@ViewChild
.fuente
ngClass
+hidden
class en lugar dengIf
. Eso funciono. ¡Gracias!El problema como se mencionó anteriormente es el
ngIf
que hace que la vista sea indefinida. La respuesta es usar enViewChildren
lugar deViewChild
. Tuve un problema similar en el que no quería que se mostrara una cuadrícula hasta que se hayan cargado todos los datos de referencia.html:
Código de componente
Aquí estamos utilizando
ViewChildren
en el que puede escuchar los cambios. En este caso, cualquier niño con la referencia#searchGrid
. Espero que esto ayude.fuente
this.SearchGrid
propiedades que debe usarsetTimeout(()=>{ ///your code here }, 1);
para evitar la sintaxis Excepción: la expresión ha cambiado después de que se verificó*ngIf
funciona, y después de renderizar podemos guardar un ElementRef de los componentes dinámicos.Podrías usar un setter para
@ViewChild()
Si tiene un contenedor ngIf, se llamará al setter con undefined, y luego nuevamente con una referencia una vez que ngIf le permita renderizar.
Sin embargo, mi problema era otra cosa. No había incluido el módulo que contenía mis "FilterTiles" en mi app.modules. La plantilla no arrojó un error, pero la referencia siempre fue indefinida.
fuente
FilterTiles
. He encontrado ese problema por esa razón antes.@ViewChild('paginator', {static: false})
Esto funcionó para mí.
Mi componente llamado 'my-component', por ejemplo, se mostró usando * ngIf = "showMe" así:
Entonces, cuando el componente se inicializa, el componente aún no se muestra hasta que "showMe" sea verdadero. Por lo tanto, mis referencias @ViewChild no estaban definidas.
Aquí es donde utilicé @ViewChildren y la QueryList que devuelve. Vea el artículo angular en QueryList y una demostración de uso de @ViewChildren .
Puede usar la Lista de consultas que devuelve @ViewChildren y suscribirse a cualquier cambio en los elementos a los que se hace referencia utilizando rxjs como se ve a continuación. @ViewChild no tiene esta habilidad.
Espero que esto ayude a alguien a ahorrar algo de tiempo y frustración.
fuente
.take(1).subscribe()
, pero excelente respuesta, ¡muchas gracias!Mi solución era usar en
[style.display]="getControlsOnStyleDisplay()"
lugar de*ngIf="controlsOn"
. El bloque está ahí pero no se muestra.fuente
Lo que resolvió mi problema fue asegurarme de que
static
estaba configurado enfalse
.Con
static
desactivado,@ViewChild
Angular actualiza la referencia cuando*ngIf
cambia la directiva.fuente
Mi solución a esto fue reemplazar
*ngIf
con[hidden]
. Lo malo era que todos los componentes secundarios estaban presentes en el código DOM. Pero funcionó para mis requisitos.fuente
En mi caso, tenía un setter de variables de entrada usando
ViewChild
, yViewChild
estaba dentro de una*ngIf
directiva, por lo que el setter intentaba acceder antes que el*ngIf
renderizado (funcionaría bien sin el*ngIf
, pero no funcionaría si siempre estuviera configurado en cierto con*ngIf="true"
).Para resolverlo, utilicé Rxjs para asegurarme de que cualquier referencia al
ViewChild
esperado hasta que se iniciara la vista. Primero, cree un Asunto que se complete después de ver init.Luego, cree una función que tome y ejecute una lambda después de que el sujeto se complete.
Finalmente, asegúrese de que las referencias a ViewChild usen esta función.
fuente
Debe funcionar
Pero como dijo Günter Zöchbauer , debe haber algún otro problema en la plantilla. He creado un poco Relevant-Plunkr-Answer . Por favor, compruebe la consola del navegador.
boot.ts
filterTiles.ts
Funciona a las mil maravillas. Por favor revise sus etiquetas y referencias.
Gracias...
fuente
Esto funciona para mí, vea el ejemplo a continuación.
fuente
Tuve un problema similar, donde
ViewChild
estaba dentro de unaswitch
cláusula que no estaba cargando el elemento viewChild antes de que fuera referenciado. Lo resolví de una manera semi-hacky pero envolviendo laViewChild
referencia en unasetTimeout
que se ejecutó de inmediato (es decir, 0 ms)fuente
Mi solución a esto fue mover el ngIf desde el exterior del componente secundario al interior del componente secundario en un div que envolvía toda la sección de html. De esa manera, todavía se ocultaba cuando era necesario, pero podía cargar el componente y podía hacer referencia a él en el padre.
fuente
Lo arreglo simplemente agregando SetTimeout después de establecer visible el componente
Mi HTML:
My Component JS
fuente
En mi caso, sabía que el componente hijo siempre estaría presente, pero quería alterar el estado antes de que el niño se inicializara para ahorrar trabajo.
Elijo probar el niño hasta que apareció y hacer cambios de inmediato, lo que me ahorró un ciclo de cambio en el componente hijo.
Alertativamente, puede agregar un número máximo de intentos o agregar algunos ms de retraso al
setTimeout
.setTimeout
efectivamente lanza la función al final de la lista de operaciones pendientes.fuente
Un tipo de enfoque genérico:
Puede crear un método que esperará hasta
ViewChild
que esté listoUso:
fuente
Para mí, el problema era que hacía referencia a la ID del elemento.
En lugar de esto:
fuente
Si usa Ionic, deberá usar el
ionViewDidEnter()
gancho del ciclo de vida. Iónica corre un poco de materia adicional (principalmente relacionados con la animación) que normalmente provoca errores inesperados como éste, de ahí la necesidad de algo que se ejecuta despuésngOnInit
,ngAfterContentInit
y así sucesivamente.fuente
Aquí hay algo que funcionó para mí.
Así que básicamente establezco una verificación cada segundo hasta que se
*ngIf
hace realidad y luego hago mis cosas relacionadas con elElementRef
.fuente
La solución que funcionó para mí fue agregar la directiva en las declaraciones en app.module.ts
fuente