Pregunta
¿Cuál es la forma más elegante de obtener @ViewChilddespués de que se muestre el elemento correspondiente en la plantilla?
A continuación se muestra un ejemplo. También Plunker disponible.
Modelo:
<div id="layout" *ngIf="display">
<div #contentPlaceholder></div>
</div>
Componente:
export class AppComponent {
display = false;
@ViewChild('contentPlaceholder', {read: ViewContainerRef}) viewContainerRef;
show() {
this.display = true;
console.log(this.viewContainerRef); // undefined
setTimeout(()=> {
console.log(this.viewContainerRef); // OK
}, 1);
}
}
Tengo un componente con su contenido oculto por defecto. Cuando alguien llama al show()método, se vuelve visible. Sin embargo, antes de que se complete la detección de cambios en Angular 2, no puedo hacer referencia a ellos viewContainerRef. Normalmente envuelvo todas las acciones requeridas setTimeout(()=>{},1)como se muestra arriba. ¿Hay una forma más correcta?
Sé que hay una opción con ngAfterViewChecked, pero causa demasiadas llamadas inútiles.
RESPUESTA (Plunker)
angular
angular2-changedetection
sinedsem
fuente
fuente

Respuestas:
Use un setter para ViewChild:
El colocador se llama con un elemento de referencia una vez que
*ngIfse conviertetrue.Tenga en cuenta que para Angular 8 debe asegurarse de configurar
{ static: false }, que es una configuración predeterminada en otras versiones de Agnular:Nota: si contentPlaceholder es un componente, puede cambiar ElementRef a su componente Clase:
fuente
contentPlaceholderes .ElementRefViewContainerRef<div #contentPlaceholder></div>. Técnicamente, puede llamarlo manualmente como cualquier otro setter,this.content = someElementRefpero no veo por qué querría hacer eso.Una alternativa para superar esto es ejecutar el detector de cambios manualmente.
Primero se inyecta el
ChangeDetectorRef:Luego lo llama después de actualizar la variable que controla el * ngIf
fuente
onInit(), así que agregué la funcióndetectChangesantes de llamar a cualquier niño y lo solucionó. (Angular 8+
Debe agregar
{ static: false }como segunda opción para@ViewChild. Esto hace que los resultados de la consulta se resuelvan después de que se ejecute la detección de cambios, lo que le permite@ViewChildactualizarse después de que cambie el valor.Ejemplo:
Ejemplo de Stackblitz: https://stackblitz.com/edit/angular-d8ezsn
fuente
<mat-horizontal-stepper *ngIf="viewMode === DialogModeEnum['New']" linear #stepper,@ViewChild('stepper', {static: true}) private stepper: MatStepper;ythis.changeDetector.detectChanges();todavía no funciona.Las respuestas anteriores no me funcionaron porque en mi proyecto, el ngIf está en un elemento de entrada. Necesitaba acceso al atributo nativeElement para centrarme en la entrada cuando ngIf es verdadero. Parece que no hay un atributo nativeElement en ViewContainerRef. Esto es lo que hice (siguiendo la documentación de @ViewChild ):
Usé setTimeout antes de enfocar porque ViewChild tarda un segundo en asignarse. De lo contrario, sería indefinido.
fuente
setTimeout?I usually wrap all required actions into setTimeout(()=>{},1) as shown above. Is there a more correct way?Como se mencionó por otro, la solución más rápida y rápida es usar [oculto] en lugar de * ng Si, de esta manera, el componente se creará pero no será visible, allí puede tener acceso a él, aunque podría no ser el más eficiente camino.
fuente
Esto podría funcionar, pero no sé si es conveniente para su caso:
fuente
ngAfterViewInit()lugar dengOnInit()? Supuse queviewContainerRefsya está inicializado pero que aún no contiene elementos. Parece que recordé esto mal.AfterViewInitEn realidad funciona. He eliminado todos mis comentarios para no confundir a las personas. Aquí hay un Plunker en funcionamiento: plnkr.co/edit/myu7qXonmpA2hxxU3SLB?p=previewconstparámetro deViewChildOtro "truco" rápido (solución fácil) es usar la etiqueta [oculta] en lugar de * ngIf, solo es importante saber que en ese caso Angular construye el objeto y lo pinta en clase: oculto es por eso que ViewChild funciona sin problemas . Por lo tanto, es importante tener en cuenta que no debe usar elementos ocultos en objetos pesados o caros que pueden causar problemas de rendimiento
fuente
Mi objetivo era evitar cualquier método hacky que suponga algo (por ejemplo, setTimeout) y terminé implementando la solución aceptada con un poco de sabor RxJS en la parte superior:
Mi escenario: quería activar una acción en un
@ViewChildelemento dependiendo del enrutadorqueryParams. Debido a que el ajuste*ngIfes falso hasta que la solicitud HTTP devuelve los datos, la inicialización del@ViewChildelemento ocurre con un retraso.Cómo funciona:
combineLatestemite un valor por primera vez solo cuando cada uno de los Observables proporcionados emite el primer valor desde quecombineLatestse suscribió el momento . Mi AsuntotabSetInitializedemite un valor cuando se establece el@ViewChildelemento. Con esto, retrasé la ejecución del código bajosubscribehasta que las*ngIfvueltas sean positivas y@ViewChildse inicialice.Por supuesto, no olvide darse de baja en ngOnDestroy, lo hago usando el
ngUnsubscribeAsunto:fuente
Una versión simplificada, tuve un problema similar al usar el SDK de Google Maps JS.
Mi solución fue extraer
divyViewChilden su propio componente hijo que, cuando se usaba en el componente padre, podía ocultarse / mostrarse usando un*ngIf.antes de
HomePageComponentModeloHomePageComponentComponenteDespués
MapComponentModeloMapComponentComponenteHomePageComponentModeloHomePageComponentComponentefuente
En mi caso, necesitaba cargar un módulo completo solo cuando el div existía en la plantilla, lo que significa que la salida estaba dentro de un ngif. De esta manera, cada vez que angular detecta el elemento #geolocalisationOutlet, crea el componente dentro de él. El módulo solo se carga una vez también.
fuente
Creo que usar diferir de lodash tiene mucho sentido, especialmente en mi caso donde
@ViewChild()estaba dentro de laasynctuberíafuente
Trabajando en Angular 8 No es necesario importar ChangeDector
ngIf le permite no cargar el elemento y evitar agregar más estrés a su aplicación. Así es como lo hice funcionar sin ChangeDetector
Luego, cuando cambio mi valor ngIf para que sea verdadero, usaría setTimeout como este para que espere solo el próximo ciclo de cambio:
Esto también me permitió evitar el uso de bibliotecas o importaciones adicionales.
fuente
para Angular 8 : una mezcla de comprobación nula y
@ViewChildstatic: falsepirateríapara un control de paginación esperando datos asíncronos
fuente
Funciona para mí si uso ChangeDetectorRef en Angular 9
fuente