¿Cómo forzar la re-representación de un componente en Angular 2?

181

¿Cómo forzar la re-representación de un componente en Angular 2? Para fines de depuración que trabajen con Redux, me gustaría obligar a un componente a volver a representar su vista, ¿es eso posible?

born2net
fuente
¿Qué quieres decir con "re-renderizado"? ¿Actualizar los enlaces?
Günter Zöchbauer
Solo una pregunta rápida: ¿por qué necesita forzar una nueva representación?
Tuong Le
55
Posible duplicado de la detección de cambio
blo0p3r

Respuestas:

217

El renderizado ocurre después de la detección de cambios. Para forzar la detección de cambios, de modo que los valores de las propiedades de los componentes que han cambiado se propaguen al DOM (y luego el navegador mostrará esos cambios en la vista), estas son algunas opciones:

  • ApplicationRef.tick () - similar a Angular 1's $rootScope.$digest()- es decir, verifique el árbol de componentes completo
  • NgZone.run (devolución de llamada) - similar a $rootScope.$apply(callback)- es decir, evaluar la función de devolución de llamada dentro de la zona Angular 2. Creo, pero no estoy seguro, que esto termina comprobando el árbol de componentes completo después de ejecutar la función de devolución de llamada.
  • ChangeDetectorRef.detectChanges () - similar a $scope.$digest()- es decir, verifique solo este componente y sus elementos secundarios

Usted tendrá que importar y luego inyectar ApplicationRef, NgZoneo ChangeDetectorRefen su componente.

Para su escenario particular, recomendaría la última opción si solo un componente ha cambiado.

Mark Rajcok
fuente
1
¿Algún código de trabajo en ChangeDetectorRef para la versión final de angular2? Ahora me enfrento a una situación en la que la vista no se actualiza después de una solicitud de publicación de http para crear un nuevo usuario y luego, con éxito, empujar el nuevo objeto a la lista de usuarios anterior existente (utilizada para iterar en la vista). Muy extraño eso this is the first time I am facing an update not working in ng2. La estrategia de detección de cambios es la predeterminada, así que sé que no me he equivocado con la estrategia de detección de cambios.
Gary
1
@ Gary, debe publicar una nueva pregunta e incluir su componente y su código de servicio (idealmente, incluir un plunker mínimo que demuestre el problema). Un problema común que he visto es no usar el thiscontexto adecuado en la devolución de llamada POST.
Mark Rajcok
¿Sabes si puedo activar manualmente las tuberías cada vez que realizo un cambio? Intenté activar la detección de cambios pero la tubería no se actualiza ... También lo intenté con pure:falsela tubería. Funciona pero es demasiado caro (ineficiente) para mi caso de uso.
ncohen
1
@ncohen, no conozco ninguna forma de activar manualmente una actualización de tubería. Puede usar una tubería pura y cambiar la referencia del objeto siempre que desee activar una actualización. Esto se discute en la sección "Pipas puras" del documento Pipes . Dependiendo de su caso de uso, es posible que desee utilizar una propiedad de componente en lugar de una tubería. Esta técnica se discute brevemente al final del documento Pipes.
Mark Rajcok
1
@ N-ate, todos los enlaces son fijos.
Mark Rajcok
47

tx, encontré la solución que necesitaba:

  constructor(private zone:NgZone) {
    // enable to for time travel
    this.appStore.subscribe((state) => {
        this.zone.run(() => {
            console.log('enabled time travel');
        });
    });

ejecutar zone.run forzará al componente a volver a renderizar

born2net
fuente
66
¿Qué es appStore en este contexto? ¿Qué tipo de variable y su tipo? parece ser observable ... pero mi observable está dentro del componente que quiero actualizar con solo hacer clic en un botón ... y no sé cómo acceder a un método / variable de componente hijo desde la ubicación principal / actual
Abdeali Chandanwala
28

Enfoque ChangeDetectorRef

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';

export class MyComponent {

    constructor(private cdr: ChangeDetectorRef) { }

    selected(item: any) {
        if (item == 'Department')
            this.isDepartment = true;
        else
            this.isDepartment = false;
        this.cdr.detectChanges();
    }

}
Feng Zhang
fuente
14

Obligo a recargar mi componente usando * ngIf.

Todos los componentes dentro de mi contenedor vuelven a los ganchos de ciclo de vida completo.

En la plantilla:

<ng-container *ngIf="_reload">
    components here 
</ng-container>

Luego en el archivo ts:

public _reload = true;

private reload() {
    setTimeout(() => this._reload = false);
    setTimeout(() => this._reload = true);
}
loonis
fuente
Gracias por esto, @loonis! Sentí que esto debería funcionar, y tenía todo excepto el setTimeout(). ¡Ahora el mío está trabajando con una solución simple y ligera!
LHM
si pudiera votar esto más de 10000 veces ..
Yazan Khalaileh
Una cosa a tener en cuenta: el contenedor que desaparece y aparece nuevamente puede causar cambios de tamaño y la página puede parpadear
ghosh
9

Otras respuestas aquí proporcionan soluciones para desencadenar ciclos de detección de cambios que actualizarán la vista del componente (que no es lo mismo que el renderizado completo).

Completa volver a hacer, lo que destruiría y reinicializar componente (llamando a todos los ganchos de ciclo de vida y la reconstrucción de la vista) se puede hacer mediante el uso ng-template, ng-containery ViewContainerRefen la siguiente forma:

<div>
  <ng-container #outlet >
  </ng-container>
</div>

<ng-template #content>
  <child></child>
</ng-template>

A continuación, en referencia a componente que tiene tanto #outlety #contentpodemos borrar el contenido de puntos de venta e insertar otra instancia de componente secundario:

@ViewChild("outlet", {read: ViewContainerRef}) outletRef: ViewContainerRef;
@ViewChild("content", {read: TemplateRef}) contentRef: TemplateRef<any>;

private rerender() {
    this.outletRef.clear();
    this.outletRef.createEmbeddedView(this.contentRef);
}

Además, el contenido inicial debe insertarse en el AfterContentInitgancho:

ngAfterContentInit() {
    this.outletRef.createEmbeddedView(this.contentRef);
}

La solución de trabajo completa se puede encontrar aquí https://stackblitz.com/edit/angular-component-rerender .

Pavel Gurecki
fuente
1

ChangeDetectorRef.detectChanges()suele ser la forma más enfocada de hacer esto. ApplicationRef.tick()Por lo general, es demasiado un enfoque de mazo.

Para usarlo ChangeDetectorRef.detectChanges(), necesitará esto en la parte superior de su componente:

import {  ChangeDetectorRef } from '@angular/core';

... entonces, usualmente alias eso cuando lo inyectas en tu constructor así:

constructor( private cdr: ChangeDetectorRef ) { ... }

Luego, en el lugar apropiado , lo llamas así:

this.cdr.detectChanges();

Donde llamasChangeDetectorRef.detectChanges() puede ser muy significativo. Usted necesita completamente comprender el ciclo de vida y exactamente cómo su aplicación está funcionando y la prestación de sus componentes. Aquí no hay sustituto para hacer su tarea por completo y asegurarse de comprender el ciclo de vida Angular al revés. Luego, una vez que comprenda eso, puede usarlo ChangeDetectorRef.detectChanges()adecuadamente (a veces es muy fácil entender dónde debe usarlo, otras veces puede ser muy complejo).

Chris Halcrow
fuente