ExpressionChangedAfterItHasBeenCheckedError: Der Ausdruck wurde geändert, nachdem er überprüft wurde. Vorheriger Wert: 'undefiniert'

76

Ich weiß, dass im Stack-Overflow bereits viele gleiche Fragen gestellt wurden, und habe verschiedene Lösungen ausprobiert, um den Laufzeitfehler zu vermeiden, aber keine davon funktioniert für mich.

Error

Komponenten- und HTML-Code

export class TestComponent implements OnInit, AfterContentChecked {
    @Input() DataContext: any;
    @Input() Position: any;
    sampleViewModel: ISampleViewModel = { DataContext: : null, Position: null };
    constructor(private validationService: IValidationService, private modalService: NgbModal, private cdRef: ChangeDetectorRef) {
    }

    ngOnInit() {

    }
    ngAfterContentChecked() {

            debugger;
            this.sampleViewModel.DataContext = this.DataContext;
            this.sampleViewModel.Position = this.Position;

    }


<div class="container-fluid sample-wrapper text-center" [ngClass]="sampleViewModel.DataContext?.Style?.CustomCssClass +' samplewidget-'+ sampleViewModel.Position?.Columns + 'x' + sampleViewModel.Position?.Rows">
     //some other html here
</div>

Bitte beachten Sie: Diese Komponente wird dynamisch mit DynamicComponentLoader geladen

Nach meiner Fehlersuche habe ich einige Probleme festgestellt

Zunächst wird diese untergeordnete Komponente dynamisch geladen, indem DynamicComponentResolver verwendet und die Eingabewerte wie folgt übergeben werden

 ngAfterViewInit() {
    this.renderWidgetInsideWidgetContainer();

  }


  renderWidgetInsideWidgetContainer() {
    let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName);
    let componentFactory = this._componentFactoryResolver.resolveComponentFactory(component);
    let viewContainerRef = this.widgetHost.viewContainerRef;
    viewContainerRef.clear();
    let componentRef = viewContainerRef.createComponent(componentFactory);
    debugger;
    (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext;
    (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position;

  }

Selbst wenn ich das HTML meiner untergeordneten Komponente wie unten geändert habe, wird der gleiche Fehler angezeigt. Fügen Sie einfach ein eckiges ngclass-Attribut hinzu

<div class="container-fluid ds-iconwidget-wrapper text-center" [ngClass]="Sample">

</div>

Meine Datenbindung und alles funktionieren einwandfrei. Muss ich irgendetwas an der übergeordneten Komponente tun? Ich habe bereits alle Lebenszyklusereignisse in der untergeordneten Komponente ausprobiert

Code-EZ
quelle
Wie fügst du hinzu TestComponent?
Max Koretskyi
1
Vor allem Bewegung this.renderWidgetInsideWidgetContainer();von ngAfterViewInitbisngOnInit
yurzui
1
@ Maximus Ihre Zeit geschätzt. Ihr Artikel ist fantastisch
Code-EZ
1
@ JEMI, Sie sind willkommen) Ich habe viele interessante Artikel, überprüfen Sie
Max Koretskyi
1
@ Maximus Sicher. Ich bin jetzt schon dein Anhänger. Ihr jeder Artikel ist sehr ausführlich.
Code-EZ

Antworten:

45

Der ngAfterContentCheckedLifecycle-Hook wird ausgelöst, wenn die Bindungsaktualisierungen für die untergeordneten Komponenten / Anweisungen bereits abgeschlossen sind. Sie aktualisieren jedoch die Eigenschaft, die als verbindliche Eingabe für die ngClassDirektive verwendet wird. Das ist das Problem. Wenn Angular die Validierungsphase ausführt, erkennt es, dass eine Aktualisierung der Eigenschaften aussteht, und löst den Fehler aus.

Lesen Sie diese beiden Artikel, um den Fehler besser zu verstehen:

Überlegen Sie, warum Sie die Eigenschaft im ngAfterViewInitLifecycle-Hook ändern müssen . Jeder andere zuvor ausgelöste Lebenszyklus ngAfterViewInit/Checkedfunktioniert beispielsweise ngOnInitoder ngDoCheckoder ngAfterContentChecked.

Um dies zu beheben, bewegen Sie sich renderWidgetInsideWidgetContainerzum ngOnInit()Lifecycle-Hook.

Max Koretskyi
quelle
Warum aktualisieren Sie die Eigenschaft in der ngAfterViewInit/Checked?
Max Koretskyi
Eigentlich wird es nicht aktualisiert. Ich setze meine Eingabedaten auf ein Ansichtsmodell und schließlich werden alle Eigenschaften wie eine Wrapper-Klasse durch dieses Modell gebunden. Wenn ich den @ Input-Datenkontext direkt binde, wird der gleiche Fehler angezeigt. Gibt es keine Problemumgehung, um diesen Fehler durch eines der Lebenszyklusereignisse zu beseitigen? Lassen Sie mich versuchen, ngAfterViewInit / Checked in Ihrer Antwort erwähnt
Code-EZ
2
versuchen Sie es ngOnInitoder ngDoCheckoder ngAfterContentChecked.Wenn es immer noch nicht funktioniert, erstellen Sie einen Plunker
Max Koretskyi
hmm..Es ist komisch für mich, nach meinem Verständnis ist es ein ziemlich einfaches Szenario. Ich vermute, es liegt am dynamischen Component Loader. Lassen Sie mich versuchen, einen Plunker zu erstellen.
Code-EZ
Ich habe alle Lebenszyklusereignisse ausprobiert, konnte das Problem jedoch leider nicht beheben. Meine Frage wird jedoch aktualisiert, indem nach der Fehlerbehebung weitere Punkte hinzugefügt werden. Ich hoffe, dies hilft Ihnen, sich ein Bild zu machen.
Code-EZ
92

Sie haben zu sagen , angulardass Sie die Inhalte aktualisiert , nachdem ngAfterContentChecked Sie importieren ChangeDetectorRefaus @angular/coreund CalldetectChanges

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

constructor( private cdref: ChangeDetectorRef ) {}


ngAfterContentChecked() {

this.sampleViewModel.DataContext = this.DataContext;
this.sampleViewModel.Position = this.Position;
this.cdref.detectChanges();

 }
Richie Fredicson
quelle
Siehe meine Kommentare in Frage Ich habe diese Lösung bereits ausprobiert, aber ich
Code-EZ
7
Ich habe AfterContentChecked importiert, das nur this.cdref.detectChanges () enthält. es funktioniert für mich danke
Mohamed Abo Elmagd
@MohamedAboElmagd Kannst du posten, wie du das zum Laufen gebracht hast?
Haseeb
1
using this.cdref.detectChanges (); nach jeder Änderung
Mohamed Abo Elmagd
Es wird möglicherweise mal aufgerufen, wenn ich Dropdown- und Hower-Elemente öffne.
Asif vora
9

Wenn Sie <ng-content>mit verwenden, müssen *ngIfSie in diese Schleife fallen.

Der einzige Ausweg, den ich fand, war die Umstellung *ngIfauf display:noneFunktionalität

Bresleveloper
quelle
oder kann [hidden] = "! condition" verwenden
Pragati Dugar
4

Zwei Lösungen:

  1. Stellen Sie sicher, dass Sie einige Codevariablen haben, und verschieben Sie diesen Code nach Settimeout ({}, 0).
  2. Verschieben Sie Ihren zugehörigen Code in die ngAfterViewInit- Methode
Chetan Laddha
quelle
Dies hängt davon ab, wie Ihr Code in die Ereigniswarteschlange verschoben wird und ausgeführt wird, wenn die aktuelle Ausführung abgeschlossen ist. JavaScript ist eine Single-Threaded-Skriptsprache, sodass es jeweils einen Code ausführen kann (aufgrund seiner Single-Threaded-Natur). Jeder dieser Codeblöcke blockiert den Fortschritt anderer asynchroner Ereignisse. Der Settimeout-Code wird erst ausgeführt, wenn Ihr aktueller Code Ihre Ausführung beendet hat.
Chetan Laddha
4
 ngAfterViewInit() {
   setTimeout(() => {
     this.renderWidgetInsideWidgetContainer();
   }, 0);
  }

Das ist eine gute Lösung, um dieses Problem zu lösen.

Станислав Тепляков
quelle
2
setTimeout(() => { // your code here }, 0);

Ich habe meinen Code in setTimeout eingewickelt und es hat funktioniert

Andris
quelle
2

* NgIf kann hier ein Problem verursachen, also verwenden Sie entweder display none css oder es ist einfacher, [hidden] = "! Condition" zu verwenden.

Pragati Dugar
quelle
Genial! Der beste und einfachste Weg!
Boshra N
2

Ich hatte Probleme mit.

FEHLER: ExpressionChangedAfterItHasBeenCheckedError: Der Ausdruck wurde geändert, nachdem er überprüft wurde. Vorheriger Wert für 'mat-checkbox-check': 'true'. Aktueller Wert: 'false'.

Das Problem hierbei ist, dass der aktualisierte Wert erst erkannt wird, wenn der nächste Änderungserkennungszyklus ausgeführt wird.

Die einfachste Lösung besteht darin, eine Änderungserkennungsstrategie hinzuzufügen. Fügen Sie diese Zeilen zu Ihrem Code hinzu:

import { ChangeDetectionStrategy } from "@angular/core";  // import

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "abc",
  templateUrl: "./abc.html",
  styleUrls: ["./abc.css"],
})
Vandana Manhas
quelle
1

Ich hatte das gleiche Problem beim Versuch, etwas das Gleiche wie Sie zu tun, und ich habe es mit etwas ähnlichem wie Richie Fredicsons Antwort behoben.

Wenn Sie createComponent () ausführen, wird es mit undefinierten Eingabevariablen erstellt. Wenn Sie danach diesen Eingabevariablen Daten zuweisen, werden die Dinge geändert und dieser Fehler in Ihrer untergeordneten Vorlage verursacht (in meinem Fall, weil ich die Eingabe in einer ngIf verwendet habe, die sich geändert hat, nachdem ich die Eingabedaten zugewiesen habe).

Die einzige Möglichkeit, dies in diesem speziellen Fall zu vermeiden, besteht darin, die Änderungserkennung zu erzwingen, nachdem Sie die Daten zugewiesen haben. In ngAfterContentChecked () habe ich dies jedoch nicht getan.

Ihr Beispielcode ist etwas schwer zu befolgen, aber wenn meine Lösung für Sie funktioniert, wäre es ungefähr so ​​(in der übergeordneten Komponente):

export class ParentComponent implements AfterViewInit {
  // I'm assuming you have a WidgetDirective.
  @ViewChild(WidgetDirective) widgetHost: WidgetDirective;

  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    private changeDetector: ChangeDetectorRef
  ) {}

  ngAfterViewInit() {
    renderWidgetInsideWidgetContainer();
  }

  renderWidgetInsideWidgetContainer() {
    let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName);
    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
    let viewContainerRef = this.widgetHost.viewContainerRef;
    viewContainerRef.clear();
    let componentRef = viewContainerRef.createComponent(componentFactory);
    debugger;
    // This <IDataBind> type you are using here needs to be changed to be the component
    // type you used for the call to resolveComponentFactory() above (if it isn't already).
    // It tells it that this component instance if of that type and then it knows
    // that WidgetDataContext and WidgetPosition are @Inputs for it.
    (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext;
    (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position;
    this.changeDetector.detectChanges();
  }
}

Meins ist fast das gleiche, außer dass ich @ViewChildren anstelle von @ViewChild verwende, da ich mehrere Hostelemente habe.

Rooby
quelle
0

Versuchen Sie dies, um Ihren Code in ngOnInit () aufzurufen.

someMethod() // emitted method call from output
{
    // Your code 
}

ngOnInit(){
  someMethod(); // call here your error will be gone
}
SURENDRANATH S.
quelle