Basically, there are 2 ways to make the dynamic injection happen.
inject values through the constructor, using a custom (or global) injector: *ngComponentOutlet="myComponent; injector: myInjector
, where myInjector
is the injector you want to use (if default injector is OK -- take it from your parent's context by adding public myInjector: Injector
in your parent component's constructor). That wasn't an option for me as I wanted to keep my dynamic (child) component's constructor clean.
make a callback that's called when the component is created, and inside that callback assign your @Input
values and subscribe your @Output
s manually. Now, ngComponentOutlet
does not provide any callback functionality, so you may have to reinvent the wheel - create an ngComponentOutlet
directive that supports callbacks. And that's exactly what this solution is all about.
I created a custom directive myComponentOutlet
(https://github.com/angular/angular/issues/15360#issuecomment-1070420494) - a customizable analogue for ngComponentOutlet
. Hopefully, it will eventually make its way to the Angular's src.
Here's the directive:
import {
AfterViewInit,
ComponentFactoryResolver,
ComponentRef,
Directive,
ElementRef,
EventEmitter,
Injector,
Input,
OnDestroy,
Output,
Type,
ViewContainerRef
} from '@angular/core';
/*
USAGE:
<div myComponentOutlet
[component]="inner.component"
[injector]="inner.injector"
(create)="componentCreated($event)"></div>
*/
@Directive({
selector: '[myComponentOutlet]',
})
export class MyComponentOutletDirective<T> implements AfterViewInit, OnDestroy {
@Input() component: Type<T>;
@Input() injector: Injector;
@Output() create = new EventEmitter<ComponentRef<T>>();
@Output() destroy = new EventEmitter<ComponentRef<T>>();
private componentRef: ComponentRef<T>;
constructor(
private viewContainerRef: ViewContainerRef,
private resolver: ComponentFactoryResolver,
private elRef: ElementRef,
private globalInjector: Injector
) {
this.injector = globalInjector;
}
ngAfterViewInit() {
const injector = this.injector || this.globalInjector;
const factory = this.resolver.resolveComponentFactory(this.component);
this.componentRef = this.viewContainerRef.createComponent(factory, 0, injector);
this.elRef.nativeElement.appendChild(this.componentRef.location.nativeElement);
this.create?.emit(this.componentRef);
}
ngOnDestroy(): void {
this.destroy?.emit(this.componentRef);
}
}
And here's the usage:
<div myComponentOutlet
[component]="inner.component"
[injector]="inner.injector"
(create)="componentCreated($event)"></div>
Adjust it to your own needs
It's not as robust as <div *ngComponentOutlet="myComponent; inputs:[...]; outputs:[...]"></div>
, but it's something and it's quite clean and it works.