it-swarm.com.de

Wie verwalte ich den Angular2 "Ausdruck hat sich geändert, nachdem er geprüft wurde" eine Ausnahme, wenn eine Komponenteneigenschaft von der aktuellen Datumszeit abhängt

Meine Komponente verfügt über Stile, die von der aktuellen Datumszeit abhängen. In meiner Komponente habe ich folgende Funktion.

  private fontColor( dto : Dto ) : string {
    // date d'exécution du dto
    let dtoDate : Date = new Date( dto.LastExecution );

    (...)

    let color =  "hsl( " + hue + ", 80%, " + (maxLigness - lightnessAmp) + "%)";

    return color;
  }

lightnessAmp wird aus der aktuellen Datumszeit berechnet. Die Farbe ändert sich, wenn sich dtoDate in den letzten 24 Stunden befindet.

Der genaue Fehler ist der folgende:

Ausdruck hat sich geändert, nachdem er geprüft wurde. Vorheriger Wert: 'hsl (123, 80%, 49%)'. Aktueller Wert: 'HSL (123, 80%, 48%)'

Ich weiß, dass die Ausnahme nur im Entwicklungsmodus erscheint, wenn der Wert überprüft wird. Wenn der geprüfte Wert vom aktualisierten Wert abweicht, wird die Ausnahme ausgelöst.

Also habe ich versucht, das aktuelle Datum in jedem Lebenszyklus mit der folgenden Hook-Methode zu aktualisieren, um die Ausnahme zu verhindern:

  ngAfterViewChecked()
  {
    console.log( "! changement de la date du composant !" );
    this.dateNow = new Date();
  }

...aber ohne Erfolg.

87

Führen Sie die Änderungserkennung explizit nach der Änderung aus:

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

constructor(private cdRef:ChangeDetectorRef) {}

ngAfterViewChecked()
{
  console.log( "! changement de la date du composant !" );
  this.dateNow = new Date();
  this.cdRef.detectChanges();
}
225

Dies ist ein schöner Beitrag, um diesen Fehler zu verstehen. Dies ist nicht zu lange zum Lesen.

32
tomonari_t

Wie von @leocaseiro in der github Ausgabe erwähnt.

Ich habe 3 Lösungen für diejenigen gefunden, die einfache Lösungen suchen.

1) Wechsel von ngAfterViewInit zu ngAfterContentInit

2) Zu ngAfterViewChecked wechseln, kombiniert mit ChangeDetectorRef als vorgeschlagen für # 14748 (Kommentar)

3) Bei ngOnInit () beibehalten, aber nach ChangeDetectorRef.detectChanges() aufrufen. Ihre Änderungen.

15
candidJ

Kleine Problemumgehung für dieses Problem:

ngAfterViewInit() { // or ngOnInit or whatever
    setTimeout(() => {
        this.dateNow = new Date();
    });
}
12
Sergei Panfilov

Ich denke, die beste und sauberste Lösung Sie können sich Folgendes vorstellen:

@Component( {
  selector: 'app-my-component',
  template: `<p>{{ myData?.anyfield }}</p>`,
  styles: [ '' ]
} )
export class MyComponent implements OnInit {
  private myData;

  constructor( private myService: MyService ) { }

  ngOnInit( ) {
    /* 
      async .. await 
      clears the ExpressionChangedAfterItHasBeenCheckedError exception.
    */
    this.myService.myObservable.subscribe(
      async (data) => { this.myData = await data }
    );
  }
}

Getestet mit Angular 5.2.9

3
JavierFuentes

In unserem Fall haben wir FIXED durch Hinzufügen von changeDetection in die Komponente und Aufrufen von detectChanges () in ngAfterContentChecked, Code wie folgt

@Component({
  selector: 'app-spinner',
  templateUrl: './spinner.component.html',
  styleUrls: ['./spinner.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpinnerComponent implements OnInit, OnDestroy, AfterContentChecked {

  show = false;

  private subscription: Subscription;

  constructor(private spinnerService: SpinnerService, private changeDedectionRef: ChangeDetectorRef) { }

  ngOnInit() {
    this.subscription = this.spinnerService.spinnerState
      .subscribe((state: SpinnerState) => {
        this.show = state.show;
      });
  }

  ngAfterContentChecked(): void {
      this.changeDedectionRef.detectChanges();
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

}

Eine kleine Arbeitsumgebung habe ich oft benutzt

Promise.resolve(null).then(() => {
    console.log( "! changement de la date du composant !" );
    this.dateNow = new Date();
    this.cdRef.detectChanges();
});

Ich ersetze hauptsächlich "Null" durch eine Variable, die ich im Controller verwende.

0
Khateeb321

Hier ist ein kleiner Auszug aus tomonari_t _ ​​ answer über die Ursachen für diesen Fehler. Ich habe versucht, nur die Teile aufzunehmen, die mir dabei geholfen haben, dies zu verstehen.

Der vollständige Artikel enthält echte Codebeispiele zu jedem hier gezeigten Punkt.

Die Hauptursache ist der Lebenszyklus des Winkels:

Nach jeder Operation merkt sich Angular, welche Werte er verwendet hat eine Operation. Sie werden in der oldValues-Eigenschaft von .__ gespeichert. Komponentensicht.

Nachdem die Prüfungen für alle Komponenten durchgeführt wurden, startet Angular Im nächsten Digest-Zyklus werden die aktuellen Werte jedoch nicht mit Vorgängen ausgeführt, sondern mit den Werten verglichen, die sie sich von .__ merken. der vorherige Digest-Zyklus.

Die folgenden Vorgänge werden beim Digest-Zyklus überprüft:

stellen Sie sicher, dass die an die untergeordneten Komponenten übergebenen Werte mit den Werten von .__ übereinstimmen. die Werte, die zum Aktualisieren der Eigenschaften dieser Komponenten verwendet werden würden jetzt.

stellen Sie sicher, dass die zum Aktualisieren der DOM-Elemente verwendeten Werte mit den Werten von .__ übereinstimmen. Die Werte, die zum Aktualisieren dieser Elemente verwendet werden würden, führen jetzt das .__ aus. gleich.

sucht nach allen untergeordneten Komponenten

Und so wird der Fehler ausgelöst, wenn die verglichenen Werte unterschiedlich sind., Blogger Max Koretskyi erklärte:

Der Täter ist immer die untergeordnete Komponente oder eine Anweisung.

Und schließlich sind hier einige Beispiele aus der realen Welt, die normalerweise diesen Fehler verursachen:

  • Geteilte Dienstleistungen 
  • Synchronous Event Broadcasting 
  • Dynamische Komponenteninstanziierung

Jedes Beispiel ist hier (plunkr) zu finden, in meinem Fall war das Problem eine dynamische Instanziierung von Komponenten. 

Aufgrund meiner eigenen Erfahrung empfehle ich allen dringend, die setTimeout-Lösung zu vermeiden, die in meinem Fall eine "fast" Endlosschleife verursachte (21 Aufrufe, bei denen ich Ihnen nicht zeigen möchte, wie man sie provoziert). 

Ich würde empfehlen, immer an Angular Lifecycle's zu denken, damit Sie berücksichtigen können, wie sie bei jeder Änderung des Wertes einer anderen Komponente beeinflusst werden. Mit diesem Fehler sagt Ihnen Angular:

Du machst das vielleicht falsch, bist du sicher, dass du Recht hast?

Der gleiche Blog sagt auch:

Häufig besteht die Lösung darin, den rechten Änderungserkennungshaken zu verwenden, um eine dynamische Komponente zu erstellen

0
Luis Limas