I want to dynamically create a template. This should be used to build a ComponentType
at runtime and place (even replace) it somewhere inside of the ho
2019 June answer
Great news! It seems that the @angular/cdk package now has first-class support for portals!
As of the time of writing, I didn't find the above official docs particularly helpful (particularly with regard to sending data into and receiving events from the dynamic components). In summary, you will need to:
Step 1) Update your AppModule
Import PortalModule
from the @angular/cdk/portal
package and register your dynamic component(s) inside entryComponents
@NgModule({
declarations: [ ..., AppComponent, MyDynamicComponent, ... ]
imports: [ ..., PortalModule, ... ],
entryComponents: [ ..., MyDynamicComponent, ... ]
})
export class AppModule { }
Step 2. Option A: If you do NOT need to pass data into and receive events from your dynamic components:
@Component({
selector: 'my-app',
template: `
`
})
export class AppComponent {
myPortal: ComponentPortal;
onClickAddChild() {
this.myPortal = new ComponentPortal(MyDynamicComponent);
}
}
@Component({
selector: 'app-child',
template: `I am a child.
`
})
export class MyDynamicComponent{
}
See it in action
Step 2. Option B: If you DO need to pass data into and receive events from your dynamic components:
// A bit of boilerplate here. Recommend putting this function in a utils
// file in order to keep your component code a little cleaner.
function createDomPortalHost(elRef: ElementRef, injector: Injector) {
return new DomPortalHost(
elRef.nativeElement,
injector.get(ComponentFactoryResolver),
injector.get(ApplicationRef),
injector
);
}
@Component({
selector: 'my-app',
template: `
`
})
export class AppComponent {
portalHost: DomPortalHost;
@ViewChild('portalHost') elRef: ElementRef;
constructor(readonly injector: Injector) {
}
ngOnInit() {
this.portalHost = createDomPortalHost(this.elRef, this.injector);
}
onClickAddChild() {
const myPortal = new ComponentPortal(MyDynamicComponent);
const componentRef = this.portalHost.attach(myPortal);
setTimeout(() => componentRef.instance.myInput
= '> This is data passed from AppComponent <', 1000);
// ... if we had an output called 'myOutput' in a child component,
// this is how we would receive events...
// this.componentRef.instance.myOutput.subscribe(() => ...);
}
}
@Component({
selector: 'app-child',
template: `I am a child. {{myInput}}
`
})
export class MyDynamicComponent {
@Input() myInput = '';
}
See it in action