Comment forcer le re-rendu d'un composant dans Angular 2?

181

Comment forcer le re-rendu d'un composant dans Angular 2? À des fins de débogage en travaillant avec Redux, je voudrais forcer un composant à restituer sa vue, est-ce possible?

born2net
la source
Qu'entendez-vous par "re-rendu". Mettre à jour les liaisons?
Günter Zöchbauer
Juste une petite question pourquoi vous devez forcer le re-rendu?
Tuong Le
5
Copie
blo0p3r

Réponses:

217

Le rendu se produit après la détection des modifications. Pour forcer la détection des changements, afin que les valeurs des propriétés des composants qui ont changé soient propagées vers le DOM (puis le navigateur rendra ces changements dans la vue), voici quelques options:

  • ApplicationRef.tick () - similaire à Angular 1 $rootScope.$digest()- c'est-à-dire, vérifiez l'arbre complet des composants
  • NgZone.run (callback) - similaire à $rootScope.$apply(callback)- ie, évalue la fonction de callback à l'intérieur de la zone Angular 2. Je pense, mais je ne suis pas sûr, que cela finit par vérifier l'arborescence complète des composants après l'exécution de la fonction de rappel.
  • ChangeDetectorRef.detectChanges () - similaire à $scope.$digest()- c'est- à -dire, ne vérifie que ce composant et ses enfants

Vous devrez importer puis injecter ApplicationRef, NgZoneou ChangeDetectorRefdans votre composant.

Pour votre scénario particulier, je recommanderais la dernière option si un seul composant a changé.

Mark Rajcok
la source
1
Un code de travail sur ChangeDetectorRef pour la version finale d'angular2? Je suis maintenant confronté à une situation où la vue n'est pas mise à jour après la demande de publication d'un http pour créer un nouvel utilisateur, puis en cas de succès en poussant le nouvel objet vers l'ancienne liste d'utilisateurs existante (utilisée pour itérer en vue). C'est assez étrange this is the first time I am facing an update not working in ng2. La stratégie de détection de changement est par défaut donc je sais que je n'ai pas foiré avec la stratégie de détection de changement.
Gary
1
@Gary, vous devriez publier une nouvelle question et inclure votre composant et votre code de service (idéalement, inclure un plunker minimal démontrant le problème). Un problème courant que j'ai vu est de ne pas utiliser le thiscontexte approprié dans le rappel POST.
Mark Rajcok
Savez-vous si je peux déclencher manuellement les tuyaux chaque fois que j'effectue une modification? J'ai essayé de déclencher la détection de changement mais le tuyau ne se met pas à jour ... J'ai également essayé avec pure:falsedans le tuyau. Cela fonctionne mais c'est beaucoup trop cher (inefficace) pour mon cas d'utilisation.
ncohen
1
@ncohen, je ne connais aucun moyen de déclencher manuellement une mise à jour du canal. Vous pouvez utiliser un canal pur et modifier la référence de l'objet chaque fois que vous souhaitez déclencher une mise à jour. Ceci est discuté dans la section "Pure Pipes" du document Pipes . Selon votre cas d'utilisation, vous souhaiterez peut-être utiliser une propriété de composant au lieu d'un tuyau. Cette technique est brièvement discutée à la fin du doc ​​Pipes.
Mark Rajcok
1
@ N-mangé, tous les liens sont fixes.
Mark Rajcok
47

tx, j'ai trouvé la solution de contournement dont j'avais besoin:

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

l'exécution de zone.run forcera le composant à effectuer un nouveau rendu

born2net
la source
6
qu'est-ce que appStore dans ce contexte - quel type de variable et son type? semble être observable ... mais mon observable est à l'intérieur du composant que je souhaite actualiser en un clic ... et je ne sais pas comment accéder à une méthode / variable de composant enfant à partir de l'emplacement parent / actuel
Abdeali Chandanwala
28

Approche 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
la source
14

Je force le rechargement de mon composant en utilisant * ngIf.

Tous les composants à l'intérieur de mon conteneur retournent aux hooks du cycle de vie complet.

Dans le modèle:

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

Puis dans le fichier ts:

public _reload = true;

private reload() {
    setTimeout(() => this._reload = false);
    setTimeout(() => this._reload = true);
}
loonis
la source
Merci pour cela, @loonis! J'avais l'impression que cela devrait fonctionner, et j'avais tout sauf pour le setTimeout(). Maintenant, le mien travaille avec une solution simple et légère!
LHM
si je pouvais voter pour cela plus de 10000 fois ..
Yazan Khalaileh
1 chose à noter - le conteneur disparaissant et
réapparaissant
9

D'autres réponses ici fournissent des solutions pour déclencher des cycles de détection de changement qui mettront à jour la vue du composant (qui n'est pas la même chose que le rendu complet).

La ré-rendu, ce qui détruirait et composants réinitialise (appelant tous les crochets du cycle de vie et vue la reconstruction) peut être fait en utilisant ng-template, ng-containeret de ViewContainerRefla manière suivante:

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

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

Ensuite, dans le composant faisant référence aux deux #outletet #contentnous pouvons effacer le contenu des points de vente et insérer une autre instance de composant enfant:

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

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

De plus, le contenu initial doit être inséré au AfterContentInitcrochet:

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

La solution de travail complète peut être trouvée ici https://stackblitz.com/edit/angular-component-rerender .

Pavel Gurecki
la source
1

ChangeDetectorRef.detectChanges()est généralement la manière la plus ciblée d'y parvenir. ApplicationRef.tick()est généralement trop une approche de masse.

Pour l'utiliser ChangeDetectorRef.detectChanges(), vous en aurez besoin en haut de votre composant:

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

... alors, généralement vous alias que lorsque vous l'injectez dans votre constructeur comme ceci:

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

Ensuite, à l' endroit approprié , vous l'appelez comme ceci:

this.cdr.detectChanges();

L'endroit où vous appelez ChangeDetectorRef.detectChanges()peut être très important. Vous devez comprendre complètement le cycle de vie et exactement comment votre application fonctionne et rend ses composants. Il n'y a pas de substitut ici pour faire complètement vos devoirs et vous assurer de comprendre le cycle de vie angulaire à fond. Ensuite, une fois que vous avez compris cela, vous pouvez l'utiliser de ChangeDetectorRef.detectChanges()manière appropriée (parfois il est très facile de comprendre où vous devez l'utiliser, d'autres fois, cela peut être très complexe).

Chris Halcrow
la source