Pregunta
¿Cuál es la forma más elegante de obtener @ViewChild
despué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
*ngIf
se 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
contentPlaceholder
es .ElementRef
ViewContainerRef
<div #contentPlaceholder></div>
. Técnicamente, puede llamarlo manualmente como cualquier otro setter,this.content = someElementRef
pero 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óndetectChanges
antes 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@ViewChild
actualizarse 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 queviewContainerRefs
ya está inicializado pero que aún no contiene elementos. Parece que recordé esto mal.AfterViewInit
En realidad funciona. He eliminado todos mis comentarios para no confundir a las personas. Aquí hay un Plunker en funcionamiento: plnkr.co/edit/myu7qXonmpA2hxxU3SLB?p=previewconst
parámetro deViewChild
Otro "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
@ViewChild
elemento dependiendo del enrutadorqueryParams
. Debido a que el ajuste*ngIf
es falso hasta que la solicitud HTTP devuelve los datos, la inicialización del@ViewChild
elemento ocurre con un retraso.Cómo funciona:
combineLatest
emite un valor por primera vez solo cuando cada uno de los Observables proporcionados emite el primer valor desde quecombineLatest
se suscribió el momento . Mi AsuntotabSetInitialized
emite un valor cuando se establece el@ViewChild
elemento. Con esto, retrasé la ejecución del código bajosubscribe
hasta que las*ngIf
vueltas sean positivas y@ViewChild
se inicialice.Por supuesto, no olvide darse de baja en ngOnDestroy, lo hago usando el
ngUnsubscribe
Asunto:fuente
Una versión simplificada, tuve un problema similar al usar el SDK de Google Maps JS.
Mi solución fue extraer
div
yViewChild
en su propio componente hijo que, cuando se usaba en el componente padre, podía ocultarse / mostrarse usando un*ngIf
.antes de
HomePageComponent
ModeloHomePageComponent
ComponenteDespués
MapComponent
ModeloMapComponent
ComponenteHomePageComponent
ModeloHomePageComponent
Componentefuente
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 laasync
tuberí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
@ViewChild
static: false
pirateríapara un control de paginación esperando datos asíncronos
fuente
Funciona para mí si uso ChangeDetectorRef en Angular 9
fuente