Wie erzwinge ich das erneute Rendern einer Komponente in Angular 2?

180

Wie erzwinge ich das erneute Rendern einer Komponente in Angular 2? Für Debug-Zwecke, die mit Redux arbeiten, möchte ich eine Komponente zwingen, ihre Ansicht neu zu rendern. Ist das möglich?

born2net
quelle
Was meinst du mit "Re-Rendering"? Bindungen aktualisieren?
Günter Zöchbauer
Nur eine kurze Frage, warum Sie das erneute Rendern erzwingen müssen.
Tuong Le
5
Mögliches Duplikat der manuellen Erkennung
blo0p3r

Antworten:

216

Das Rendern erfolgt nach der Änderungserkennung. Um die Änderungserkennung zu erzwingen, damit geänderte Werte von Komponenteneigenschaften an das DOM weitergegeben werden (und der Browser diese Änderungen dann in der Ansicht rendert), haben Sie folgende Optionen:

  • ApplicationRef.tick () - ähnlich wie bei Angular 1 $rootScope.$digest()- dh überprüfen Sie den vollständigen Komponentenbaum
  • NgZone.run (Rückruf) - ähnlich wie $rootScope.$apply(callback)- dh die Rückruffunktion innerhalb der Angular 2-Zone auswerten. Ich denke, aber ich bin nicht sicher, ob dies dazu führt, dass der vollständige Komponentenbaum nach Ausführung der Rückruffunktion überprüft wird.
  • ChangeDetectorRef.detectChanges () - ähnlich wie $scope.$digest()- dh nur diese Komponente und ihre untergeordneten Komponenten überprüfen

Sie müssen importieren und dann spritzen ApplicationRef, NgZoneoderChangeDetectorRef in Ihrer Komponente.

Für Ihr spezielles Szenario würde ich die letzte Option empfehlen, wenn sich nur eine einzelne Komponente geändert hat.

Mark Rajcok
quelle
1
Gibt es einen Arbeitscode für ChangeDetectorRef für die endgültige Version von angle2? Ich bin jetzt mit einer Situation konfrontiert, in der die Ansicht nach der Post-Anforderung eines http zum Erstellen eines neuen Benutzers nicht aktualisiert wird und das neue Objekt dann bei Erfolg auf die vorhandene alte Benutzerliste verschoben wird (die zum Iterieren in der Ansicht verwendet wird). Ganz seltsam das this is the first time I am facing an update not working in ng2. Die Änderungserkennungsstrategie ist Standard, daher weiß ich, dass ich die Änderungserkennungsstrategie nicht durcheinander gebracht habe.
Gary
1
@ Gary, Sie sollten eine neue Frage stellen und Ihre Komponente und Ihren Servicecode angeben (idealerweise einen minimalen Plunker, der das Problem demonstriert). Ein häufiges Problem, das ich gesehen habe, ist die Verwendung des richtigen thisKontexts im POST-Rückruf.
Mark Rajcok
Wissen Sie, ob ich die Pipes bei jeder Änderung manuell auslösen kann? Ich habe versucht, die Änderungserkennung auszulösen, aber die Pipe wird nicht aktualisiert ... Ich habe es auch mit pure:falsein der Pipe versucht . Es funktioniert, ist aber für meinen Anwendungsfall viel zu teuer (ineffizient).
Ncohen
1
@ncohen, mir ist keine Möglichkeit bekannt, ein Pipe-Update manuell auszulösen. Sie können eine reine Pipe verwenden und die Objektreferenz ändern, wenn Sie ein Update auslösen möchten. Dies wird im Abschnitt "Reine Rohre" des Pipes-Dokuments erläutert . Abhängig von Ihrem Anwendungsfall möchten Sie möglicherweise eine Komponenteneigenschaft anstelle einer Pipe verwenden. Diese Technik wird am Ende des Pipes-Dokuments kurz erläutert.
Mark Rajcok
1
@ N-ate, alle Links sind behoben.
Mark Rajcok
47

tx, fand die Problemumgehung, die ich brauchte:

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

Wenn Sie zone.run ausführen, wird die Komponente zum erneuten Rendern gezwungen

born2net
quelle
6
Was ist AppStore in diesem Zusammenhang - welche Art von Variable und welcher Typ? scheint beobachtbar zu sein ... aber mein beobachtbares Element befindet sich in der Komponente, die ich auf Knopfdruck
aktualisieren möchte
28

ChangeDetectorRef-Ansatz

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
quelle
13

Ich erzwinge das Neuladen meiner Komponente mit * ngIf.

Alle Komponenten in meinem Container gehen auf die Hooks für den gesamten Lebenszyklus zurück.

In der Vorlage:

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

Dann in der ts-Datei:

public _reload = true;

private reload() {
    setTimeout(() => this._reload = false);
    setTimeout(() => this._reload = true);
}
Loonis
quelle
Danke dafür, @loonis! Ich hatte das Gefühl, dass dies funktionieren sollte, und ich hatte alles außer dem setTimeout(). Jetzt arbeitet meine mit einer einfachen und leichten Lösung!
LHM
wenn ich dies mehr als 10000 Mal positiv bewerten könnte ..
Yazan Khalaileh
9

Andere Antworten hier bieten Lösungen zum Auslösen von Änderungserkennungszyklen, mit denen die Ansicht der Komponente aktualisiert wird (was nicht mit dem vollständigen erneuten Rendern identisch ist).

Vollständige Wieder macht, die zerstören und neu zu initialisieren Komponente (Aufruf all Lifecycle - Haken und Wiederaufbau Ansicht) kann durch die Verwendung durchgeführt werden ng-template, ng-containerund ViewContainerRefin folgenden Weise:

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

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

Dann in der Komponente Bezug auf den beiden #outletund #contentwir können Steckdosen Inhalt löschen und eine neue Instanz der Kinderkomponente einfügen:

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

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

Zusätzlich sollte der anfängliche Inhalt am AfterContentInitHaken eingefügt werden :

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

Eine voll funktionsfähige Lösung finden Sie hier https://stackblitz.com/edit/angular-component-rerender .

Pavel Gurecki
quelle
1

ChangeDetectorRef.detectChanges() ist normalerweise der konzentrierteste Weg, dies zu tun. ApplicationRef.tick()ist in der Regel zu viel Vorschlaghammer Ansatz.

Zur Verwendung ChangeDetectorRef.detectChanges()benötigen Sie dies oben in Ihrer Komponente:

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

... dann haben Sie normalerweise den Alias, wenn Sie es wie folgt in Ihren Konstruktor injizieren:

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

Dann nennen Sie es an der richtigen Stelle so:

this.cdr.detectChanges();

Wo Sie anrufen, ChangeDetectorRef.detectChanges()kann von großer Bedeutung sein. Sie müssen den Lebenszyklus und die Funktionsweise Ihrer Anwendung vollständig verstehen und ihre Komponenten rendern. Es gibt hier keinen Ersatz dafür, Ihre Hausaufgaben vollständig zu erledigen und sicherzustellen, dass Sie den Angular-Lebenszyklus von innen nach außen verstehen. Sobald Sie das verstanden haben, können Sie es ChangeDetectorRef.detectChanges()angemessen verwenden (manchmal ist es sehr einfach zu verstehen, wo Sie es verwenden sollten, manchmal kann es sehr komplex sein).

Chris Halcrow
quelle