web-dev-qa-db-fra.com

ExpressionChangedAfterItHasBeenCheckedError: L'expression a changé après avoir été vérifiée. Valeur précédente: 'undefined'

Je sais qu'il y a beaucoup de questions identiques déjà postées dans stackoverflow et que nous avons essayé différentes solutions pour éviter l'erreur d'exécution, mais aucune d'entre elles ne fonctionne pas pour moi.

 Error

Composant et code HTML

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>

Remarque: ce composant est chargé de manière dynamique à l'aide de DynamicComponentLoader} _

Après mes problèmes de tournage, j'ai identifié quelques problèmes

Tout d'abord, ce composant enfant est chargé dynamiquement à l'aide de DynamicComponentResolver et en transmettant les valeurs d'entrée comme ci-dessous.

 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;

  }

Même si j'ai changé le code HTML de mon composant enfant, comme ci-dessous, je reçois la même erreur. Il suffit d'ajouter un attribut angular ngclass

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

</div>

Ma liaison de données et tout fonctionnent correctement. Dois-je faire quelque chose sur le composant parent? J'ai déjà essayé tous les événements de cycle de vie du composant enfant

24
Code-EZ

Le hook ngAfterContentChecked lifecycle est déclenché lorsque les mises à jour des liaisons pour les composants/directives enfants ont déjà été terminées. Mais vous mettez à jour la propriété utilisée comme entrée de liaison pour la directive ngClass. C'est le problème. Lorsque Angular exécute l'étape de validation, il détecte qu'une mise à jour des propriétés est en attente et renvoie l'erreur.

Pour mieux comprendre l'erreur, lisez ces deux articles:

Réfléchissez à la raison pour laquelle vous devez modifier la propriété dans le hook ngAfterViewInit lifecycle. Tout autre cycle de vie déclenché avant ngAfterViewInit/Checked fonctionnera, par exemple ngOnInit ou ngDoCheck ou ngAfterContentChecked

Donc, pour résoudre ce problème, déplacez renderWidgetInsideWidgetContainer sur le hook ngOnInit() lifecycle.

15

vous devez indiquer à angular que vous avez mis à jour le contenu après ngAfterContentChecked vous pouvez importer ChangeDetectorRef de @angular/core et appeler detectChanges

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

constructor( private cdref: ChangeDetectorRef ) {}


ngAfterContentChecked() {

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

 }
21
Richie Fredicson

Si vous utilisez <ng-content> avec *ngIf, vous êtes tenu de tomber dans cette boucle.

La seule issue que j'ai trouvée était de changer la fonctionnalité *ngIf en display:none

0
bresleveloper

J'avais le même problème en essayant de faire la même chose que vous et je l'ai corrigé avec quelque chose de similaire à la réponse de Richie Fredicson.

Lorsque vous exécutez createComponent (), il est créé avec des variables d'entrée non définies . Ensuite, lorsque vous affectez des données à ces variables d'entrée, les choses changent et provoquent cette erreur dans votre modèle enfant (dans mon cas, c'est parce que j'utilisais entrée dans un ngIf, qui a changé une fois que j'ai attribué les données d'entrée).

La seule façon de l'éviter dans ce cas particulier est de forcer la détection des modifications après avoir affecté les données, mais je ne l'ai pas fait dans ngAfterContentChecked ().

Votre exemple de code est un peu difficile à suivre, mais si ma solution fonctionne pour vous, cela ressemblerait à ceci (dans le composant parent):

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

Le mien est presque identique à celui sauf que j'utilise @ViewChildren au lieu de @ViewChild car j'ai plusieurs éléments Host.

0
rooby

Deux solutions:

  1. Assurez-vous que vous avez des variables de liaison puis déplacez ce code vers settimeout ({}, 0);
  2. Déplacez votre code associé vers ngAfterViewInit method
0
Chetan Laddha