Come forzare il rendering di un componente in Angular 2?


181

Come forzare il rendering di un componente in Angular 2? Ai fini del debug, lavorando con Redux, vorrei forzare un componente per renderizzare nuovamente la visualizzazione, è possibile?


Cosa intendi con "re-rendering". Aggiornare gli attacchi?
Günter Zöchbauer,

Solo una domanda veloce sul perché è necessario forzare il rendering?
Tuong Le

5
Possibile duplicato dell'attivazione manuale del rilevamento delle modifiche
Angular2

Risposte:


217

Il rendering avviene dopo il rilevamento delle modifiche. Per forzare il rilevamento delle modifiche, in modo che i valori delle proprietà dei componenti che sono stati modificati vengano propagati al DOM (e quindi il browser renderà tali modifiche nella vista), ecco alcune opzioni:

  • ApplicationRef.tick () - simile agli Angular 1 $rootScope.$digest()- ovvero, controlla l'albero dei componenti completo
  • NgZone.run (callback) - simile a $rootScope.$apply(callback)- cioè, valuta la funzione di callback all'interno della zona Angular 2. Penso, ma non sono sicuro, che questo finisca per controllare l'albero dei componenti completo dopo aver eseguito la funzione di callback.
  • ChangeDetectorRef.detectChanges () - simile a $scope.$digest()- cioè, controlla solo questo componente e i suoi figli

Sarà necessario importare e poi iniettare ApplicationRef, NgZoneo ChangeDetectorRefnel componente.

Per il tuo particolare scenario, consiglierei l'ultima opzione se è cambiato solo un singolo componente.


1
Qualche codice funzionante su ChangeDetectorRef per la versione finale di angular2? Ora sto affrontando una situazione in cui la vista non viene aggiornata dopo la richiesta di posta di un http per creare un nuovo utente e quindi, in caso di successo, spingere il nuovo oggetto nella vecchia lista utenti esistente (utilizzata per scorrere in vista). È abbastanza strano this is the first time I am facing an update not working in ng2. La strategia di rilevamento delle modifiche è predefinita, quindi so di non aver sbagliato con la strategia di rilevamento delle modifiche.
Gary,

1
@Gary, dovresti pubblicare una nuova domanda e includere il componente e il codice di servizio (idealmente, includere un plunker minimo che dimostri il problema). Un problema comune che ho riscontrato non è l'utilizzo del thiscontesto corretto nel callback POST.
Mark Rajcok,

Sai se posso attivare manualmente i tubi ogni volta che eseguo una modifica? Ho provato ad attivare il rilevamento delle modifiche ma la pipe non si aggiorna ... Ho anche provato con pure:falsela pipe. Funziona ma è troppo costoso (inefficiente) per il mio caso d'uso.
ncohen,

1
@ncohen, non sono a conoscenza di alcun modo per attivare manualmente un aggiornamento della pipe. È possibile utilizzare una pipe pura e modificare il riferimento all'oggetto ogni volta che si desidera attivare un aggiornamento. Questo è discusso nella sezione "Tubi puri" del doc . A seconda del caso d'uso, è possibile che si desideri utilizzare una proprietà del componente anziché una pipe. Questa tecnica è discussa brevemente alla fine del documento Pipes.
Mark Rajcok,

1
@ N-ate, tutti i collegamenti sono stati corretti.
Mark Rajcok,

47

tx, ho trovato la soluzione alternativa di cui avevo bisogno:

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

l'esecuzione di zone.run forza il rendering del componente


6
che cos'è l'appStore in questo contesto: quale tipo di variabile e il suo tipo? sembra essere osservabile ... ma il mio osservabile è all'interno del componente che voglio aggiornare con un clic di un pulsante ... e non so come accedere a un metodo / variabile componente figlio dalla posizione principale / corrente
Abdeali Chandanwala

28

Approccio 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();
    }

}

14

Forzo a ricaricare il mio componente usando * ngIf.

Tutti i componenti all'interno del mio contenitore risalgono ai ganci del ciclo di vita completo.

Nel modello:

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

Quindi nel file ts:

public _reload = true;

private reload() {
    setTimeout(() => this._reload = false);
    setTimeout(() => this._reload = true);
}

Grazie per questo, @loonis! Sentivo che avrebbe dovuto funzionare, e avevo tutto tranne il setTimeout(). Ora il mio sta lavorando con una soluzione semplice e leggera!
LHM,

se potessi votare oltre 10000 volte ..
Yazan Khalaileh,

1 cosa da notare: il contenitore che scompare e
riapparire

9

Altre risposte qui forniscono soluzioni per l'attivazione di cicli di rilevamento delle modifiche che aggiorneranno la vista del componente (che non è la stessa del nuovo rendering completo).

Completa ri-renderizzare, che avrebbe distrutto e reinizializzare componente (chiamando tutti i ganci del ciclo di vita e la ricostruzione di vista) può essere fatto utilizzando ng-template, ng-containere ViewContainerRefin questo modo:

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

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

Quindi nel componente con riferimento a entrambi #outlete #contentpossiamo cancellare il contenuto dei punti vendita e inserire un'altra istanza del componente figlio:

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

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

Inoltre, il contenuto iniziale deve essere inserito al AfterContentInitgancio:

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

La soluzione di lavoro completa è disponibile qui https://stackblitz.com/edit/angular-component-rerender .


1

ChangeDetectorRef.detectChanges()di solito è il modo più mirato per farlo. ApplicationRef.tick()di solito è troppo un approccio con la mazza.

Per utilizzarlo ChangeDetectorRef.detectChanges(), ti servirà nella parte superiore del componente:

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

... quindi, di solito alias che quando lo iniettate nel vostro costruttore in questo modo:

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

Quindi, nel posto appropriato , lo chiami in questo modo:

this.cdr.detectChanges();

Il luogo in cui chiami ChangeDetectorRef.detectChanges()può essere molto significativo. È necessario comprendere completamente il ciclo di vita ed esattamente come funziona l'applicazione e renderizzarne i componenti. Non c'è sostituto qui per fare completamente i compiti e assicurarsi di aver compreso il ciclo di vita angolare. Quindi, una volta capito, puoi usarlo in modo ChangeDetectorRef.detectChanges()appropriato (a volte è molto facile capire dove dovresti usarlo, altre volte può essere molto complesso).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.