angular - Angular4 Cómo saber cuándo un ViewChild ha sido reiniciado
Aquí está la plantilla de main.html:
<button (click)="displayAndUseMyCustomComp()"></button>
<my-custom-comp *ngIf="isMyCustomCompDisplayed" #myCustomComp="myCustomComp"></my-custom-comp>
y main.component.ts :
export class MainComponent {
constructor() {}
private isMyCustomCompDisplayed boolean = false
@ViewChild('myCustomComp') myCustomComp: MyCustomComp
displayAndUseMyCustomComp() {
this.isMyCustomCompDisplayed = true
console.log(this.myCustomComp) // prints undefined
setTimeout(() => {
console.log(this.myCustomComp) // prints {object}
})
}
}
Lo que está sucediendo es que mi plantilla aún no se actualiza después de establecer isMyCustomCompDisplayed en true. Sin embargo, si uso un setTimeout, myCustomComp se actualiza y mi problema desaparece. Es un poco poco ortodoxo, y me preguntaba cuál era la manera correcta de hacer lo que estoy intentando.
Respuesta #1
Aquí está por qué es indefinido después de que llamas a displayAndUseMyCustomComp
Angular actualiza la lista de consultas ViewChild como parte de la detección de cambios. Cuando Angular estaba ejecutando la detección de cambios inicial, el isMyCustomCompDisplayed era false y, por lo tanto, myCustomComp estaba oculto. El myCustomComp se estableció en undefined.
Después de hacer clic, se ejecuta la función displayAndUseMyCustomComp y isMyCustomCompDisplayed se establece en true. Angular requiere un ciclo de detección de cambios para actualizar la lista de consultas myCustomComp. Sin embargo, intentas leer el valor de inmediato y, por lo tanto, todavía es undefined. Necesitas esperar otro ciclo de detección de cambios para que Angular actualice la lista de consultas.
Aquí está la razón por la que funciona si envuelves la llamada en setTimeout
Si intentas leer el myCustomComp dentro del timeout, Angular tiene la oportunidad de ejecutar la detección de cambios entre la actualización de isMyCustomCompDisplayed y el momento en que lees myCustomComp.
Aquí está lo que sucede durante ese ciclo de detección de cambios
Cuando Angular ejecuta la detección de cambios para el MainComponent, detecta que isMyCustomCompDisplayed se ha actualizado. Entonces procede a actualizar los enlaces para ngIf. Esto, a su vez, reacciona a este cambio y crea una vista incorporada con el myCustomComp y la adjunta al componente MainComponent:
@Input()
set ngIf(condition: any) {
if (condidition) {
this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
¿Cuándo está disponible la lista de consultas actualizada?
Si estás buscando una solución síncrona, estará disponible dentro de todos los hooks del ciclo de vida que se ejecutan después de que se actualice la lista de consulta de los hijos de la vista durante el siguiente ciclo de detección de cambios que sigue a la ejecución de displayAndUseMyCustomComp. Por el momento, estos son ngAfterViewInit y ngAfterViewChecked. Dado que el primero se llama solo una vez, necesitamos usar ngAfterViewChecked:
ngAfterViewChecked() {
if(this.isMyCustomCompDisplayed) {
console.log(this.myCustomComp) // prints {object}
}
}
displayAndUseMyCustomComp() {
this.isMyCustomCompDisplayed = true
}
Otra solución sincrónica sugerida por @ThinkingMedia también es buena. Puedes usar ViewChildren en lugar de ViewChild y suscribirte a changes (por cierto, no haces referencia a la plantilla):
@ViewChildren(myCustomComp) as: QueryList<myCustomComp>;
ngAfterViewInit() {
this.myCustomComp.changes.subscribe(() => {
console.log(this.myCustomComp.first); // prints {object}
});
}
La devolución de llamada se activará durante la próxima digestión cuando Angular esté actualizando la lista de consultas (un poco antes que ngAfterViewChecked).
Si estás buscando una solución asincrónica, usa setTimeout tal como lo haces. La solución Promise.resolve(null).then(()=>{ console.log(this.myCustomComp) }) (microtarea) no funcionará porque se ejecutará después de la pila actual pero antes de la detección de cambios.
Para más información sobre la detección de cambios, lea
Todo lo que necesita saber sobre la detección de cambios en Angular.
