私はすでにスタックオーバーフローに投稿された同じ質問がたくさんあり、ランタイムエラーを回避するためにさまざまな解決策を試しましたが、それらのどれも私のために働いていません。
コンポーネントと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>
注意:このコンポーネントはDynamicComponentLoaderを使用して動的にロードされます
トラブルシューティングの後、いくつかの問題を特定しました
まず、この子コンポーネントは、DynamicComponentResolverを使用して以下のような入力値を渡すことで動的にロードされます
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;
}
子コンポーネントのhtmlを次のように変更しても、同じエラーが発生します。angular ngclass属性を追加するだけです
<div class="container-fluid ds-iconwidget-wrapper text-center" [ngClass]="Sample">
</div>
データバインディングとすべてが正常に機能していますが、親コンポーネントで何かする必要がありますか?私はすでに子コンポーネントのすべてのライフサイクルイベントを試しました
ngAfterContentChecked
ライフサイクルフックは、子コンポーネント/ディレクティブのバインディングの更新が既に完了しているときにトリガーされます。ただし、ngClass
ディレクティブのバインディング入力として使用されるプロパティを更新しています。それは問題。 Angularが検証ステージを実行すると、プロパティへの保留中の更新があることを検出し、エラーをスローします。
エラーをよりよく理解するには、次の2つの記事を読んでください。
ngAfterViewInit
ライフサイクルフックでプロパティを変更する必要がある理由を考えてください。 ngAfterViewInit/Checked
の前にトリガーされる他のライフサイクル、たとえばngOnInit
またはngDoCheck
またはngAfterContentChecked
は機能します。
それを修正するには、renderWidgetInsideWidgetContainer
をngOnInit()
ライフサイクルフックに移動します。
angular
の後にコンテンツを更新したことをngAfterContentChecked
に伝える必要があります。ChangeDetectorRef
を@angular/core
からインポートしてdetectChanges
を呼び出すことができます
import {ChangeDetectorRef } from '@angular/core';
constructor( private cdref: ChangeDetectorRef ) {}
ngAfterContentChecked() {
this.sampleViewModel.DataContext = this.DataContext;
this.sampleViewModel.Position = this.Position;
this.cdref.detectChanges();
}
<ng-content>
で*ngIf
を使用している場合、このループに陥ることになります。
私が見つけた唯一の方法は、*ngIf
をdisplay:none
機能に変更することでした
私はあなたと同じことをしようと同じ問題を抱えていたので、リッチー・フレディソンの答えに似た何かでそれを修正しました。
CreateComponent()を実行すると、未定義の入力変数で作成されます。その後、それらの入力変数にデータを割り当てると、物事が変わり、子テンプレートでそのエラーが発生します(私の場合は、入力データを割り当てた後に変更されたngIfで入力を使用していたためです)。
この特定のケースで回避できる唯一の方法は、データを割り当てた後に強制的に変更を検出することですが、ngAfterContentChecked()でそれをしませんでした。
あなたのサンプルコードを追うのは少し難しいですが、私の解決策があなたのために働くなら、それはこのようなものです(親コンポーネントで):
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();
}
}
私は複数のHost要素を持っているので、@ ViewChildの代わりに@ViewChildrenを使用していることを除いて、ほとんど同じです。
2つのソリューション:
setTimeout(() => { // your code here }, 0);
コードをsetTimeoutでラップし、動作しました
ngAfterViewInit() {
setTimeout(() => {
this.renderWidgetInsideWidgetContainer();
}, 0);
}
これは、この問題を解決するための優れたソリューションです。