Jak wymusić ponowne renderowanie komponentu w Angular 2?


181

Jak wymusić ponowne renderowanie komponentu w Angular 2? Do celów debugowania, pracując z Redux, chciałbym zmusić komponent do ponownego renderowania widoku, czy to możliwe?


Co masz na myśli przez „ponowne renderowanie”. Zaktualizować wiązania?
Günter Zöchbauer

Tylko krótkie pytanie, dlaczego musisz wymusić ponowne renderowanie?
Tuong Le

Odpowiedzi:


217

Renderowanie następuje po wykryciu zmiany. Aby wymusić wykrywanie zmian, aby wartości właściwości komponentów, które uległy zmianie, były propagowane do modelu DOM (a następnie przeglądarka wyrenderuje te zmiany w widoku), oto kilka opcji:

  • ApplicationRef.tick () - podobnie jak w Angular 1 $rootScope.$digest()- tzn. Sprawdź pełne drzewo komponentów
  • NgZone.run (callback) - podobnie jak $rootScope.$apply(callback)- tzn. Ocenia funkcję callback w strefie Angular 2. Myślę, ale nie jestem pewien, czy kończy się to sprawdzaniem pełnego drzewa komponentów po wykonaniu funkcji zwrotnej.
  • ChangeDetectorRef.detectChanges () - podobnie jak $scope.$digest()- tzn. Sprawdza tylko ten składnik i jego dzieci

Trzeba będzie importować, a następnie wstrzyknąć ApplicationRef, NgZonealbo ChangeDetectorRefdo swojego urządzenia.

W przypadku twojego konkretnego scenariusza zalecałbym ostatnią opcję, jeśli zmienił się tylko jeden komponent.


1
Jakiś działający kod na ChangeDetectorRef dla ostatecznej wersji angular2? Mam teraz do czynienia z sytuacją, w której widok nie jest aktualizowany po żądaniu postu http w celu utworzenia nowego użytkownika, a następnie po pomyślnym przeniesieniu nowego obiektu do istniejącej starej listy użytkowników (używanej do iteracji w widoku). To dość dziwne this is the first time I am facing an update not working in ng2. Strategia wykrywania zmian jest domyślna, więc wiem, że nie zepsułem strategii wykrywania zmian.
Gary,

1
@Gary, powinieneś zadać nowe pytanie i dołączyć swój komponent oraz kod usługi (najlepiej, dołącz minimalny plunker demonstrujący problem). Częstym problemem, który widziałem, jest niewłaściwe użycie thiskontekstu w wywołaniu zwrotnym POST.
Mark Rajcok,

Czy wiesz, czy mogę ręcznie wyzwalać rury za każdym razem, gdy wprowadzam zmianę? Próbowałem wywołać wykrywanie zmian, ale potok się nie aktualizuje ... Próbowałem też z pure:falsew rurze. Działa, ale jest zbyt drogi (nieefektywny) dla mojego przypadku użycia.
ncohen

1
@ncohen, nie znam żadnego sposobu ręcznego wyzwalania aktualizacji potoku. Możesz użyć czystej potoku i zmienić odniesienie do obiektu, gdy chcesz wyzwolić aktualizację. Jest to omówione w sekcji „Pure Pipes” dokumentu Pipes . W zależności od przypadku użycia możesz chcieć użyć właściwości komponentu zamiast potoku. Technika ta została pokrótce omówiona na końcu dokumentu Pipes.
Mark Rajcok

1
@ N-ate, wszystkie linki są naprawione.
Mark Rajcok,

47

tx, znalazłem obejście, którego potrzebowałem:

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

Running zone.run wymusi ponowne renderowanie komponentu


6
czym jest appStore w tym kontekście - jaki rodzaj zmiennej i jej typ? wydaje się być obserwowalny ... ale moja obserwowalna znajduje się wewnątrz komponentu, który chcę odświeżyć jednym kliknięciem ... i nie wiem, jak uzyskać dostęp do metody / zmiennej składnika podrzędnego z nadrzędnej / bieżącej lokalizacji
Abdeali Chandanwala

28

Podejście 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

Wymuszam ponowne załadowanie komponentu za pomocą * ngIf.

Wszystkie komponenty w moim kontenerze wracają do haków pełnego cyklu życia.

W szablonie:

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

Następnie w pliku ts:

public _reload = true;

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

Dzięki za to, @loonis! Czułem, że to powinno działać i miałem wszystko oprócz setTimeout(). Teraz mój pracuje z prostym i lekkim rozwiązaniem!
LHM

gdybym mógł zagłosować za tym ponad 10000 razy ..
Yazan Khalaileh

1 uwaga - zniknięcie kontenera i ponowne pojawienie się może spowodować zmiany rozmiaru, a strona może migotać
ghosh

9

Inne odpowiedzi tutaj zawierają rozwiązania do wyzwalania cykli wykrywania zmian, które aktualizują widok komponentu (co nie jest tym samym, co pełne ponowne renderowanie).

Pełna re-render, który zniszczy i ponownie zainicjować składnik (nazywając wszystkie haki cyklu życia i odbudowy widok) mogą być wykonane przy użyciu ng-template, ng-containera ViewContainerRefw następujący sposób:

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

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

Następnie w komponencie mającym odniesienie do obu #outleti #contentmożemy wyczyścić zawartość outletów i wstawić kolejną instancję komponentu potomnego:

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

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

Dodatkowo początkową treść należy wstawić na AfterContentInithook:

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

Pełne działające rozwiązanie można znaleźć tutaj https://stackblitz.com/edit/angular-component-render .


1

ChangeDetectorRef.detectChanges() jest zwykle najbardziej skoncentrowanym sposobem na zrobienie tego. ApplicationRef.tick()jest zwykle zbyt dużym podejściem do młota kowalskiego.

Aby go użyć ChangeDetectorRef.detectChanges(), potrzebujesz tego u góry swojego komponentu:

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

... wtedy zwykle nazywasz to aliasem, kiedy wstrzykujesz to w konstruktorze w ten sposób:

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

Następnie w odpowiednim miejscu nazywasz to tak:

this.cdr.detectChanges();

To, gdzie dzwonisz, ChangeDetectorRef.detectChanges()może mieć duże znaczenie. Musisz w pełni zrozumieć cykl życia i dokładnie jak działa Twoja aplikacja i renderuje jej komponenty. Nic nie zastąpi tutaj całkowitego odrabiania pracy domowej i upewnienia się, że rozumiesz cykl życia Angular na wylot. Następnie, gdy już to zrozumiesz, możesz używać go ChangeDetectorRef.detectChanges()odpowiednio (czasami bardzo łatwo jest zrozumieć, gdzie należy go użyć, innym razem może to być bardzo złożone).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.