After reading a helpful post by @Günter Zöchbauer, I was able to tackle a similar issue in a unique way. I hope this solution can be of use to you as well.
To start off, I outlined a few interfaces:
// Any component loaded dynamically must adhere to this interface
export interface IDynamicComponent { Context: object; }
// Data passed from parent to dynamically loaded component
export interface IDynamicComponentData {
component: any;
context?: object;
caller?: any;
}
Next, I implemented these interfaces within the dynamically loaded component.
dynamicLoadedComponentA.ts
// ...
export class DynamicLoadedComponentA implements IDynamicComponent {
// ...
// Data from parent
public Context: object;
// ...
Following that, I created a new component responsible for handling the dynamic behavior. It was crucial to register all dynamically loaded components as entryComponents.
dynamic.component.ts
@Component({
selector: 'ngc-dynamic-component',
template: ´<ng-template #dynamicContainer></ng-template>´,
entryComponents: [ DynamicLoadedComponentA ]
})
export class DynamicComponent implements OnInit, OnDestroy, OnChanges {
@ViewChild('dynamicContainer', { read: ViewContainerRef }) public dynamicContainer: ViewContainerRef;
@Input() public componentData: IDynamicComponentData;
private componentRef: ComponentRef<any>;
private componentInstance: IDynamicComponent;
constructor(private resolver: ComponentFactoryResolver) { }
public ngOnInit() {
this.createComponent();
}
public ngOnChanges(changes: SimpleChanges) {
if (changes['componentData']) {
this.createComponent();
}
}
public ngOnDestroy() {
if (this.componentInstance) {
this.componentInstance = null;
}
if (this.componentRef) {
this.componentRef.destroy();
}
}
private createComponent() {
this.dynamicContainer.clear();
if (this.componentData && this.componentData.component) {
const factory: ComponentFactory<any> = this.resolver.resolveComponentFactory(this.componentData.component);
this.componentRef = this.dynamicContainer.createComponent(factory);
this.componentInstance = this.componentRef.instance as IDynamicComponent;
// Fill context data
Object.assign(this.componentInstance.Context, this.componentData.context || {});
// Register output events
// this.componentRef.instance.outputTrigger.subscribe(event => console.log(event));
}
}
}
Here is an example of how to use this new functionality:
app.html
<!-- [...] -->
<div>
<ngc-dynamic-component [componentData]="_settingsData"></ngc-dynamic-component>
</div>
<!-- [...] -->
app.ts
// ...
private _settingsData: IDynamicComponent = {
component: DynamicLoadedComponentA,
context: { SomeValue: 42 },
caller: this
};
// ...