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.
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
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:
ExpressionChangedAfterItHasBeenCheckedError
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.
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();
}
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
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.
Deux solutions: