I would like to use the new Portal from material CDK to inject dynamic content in multiple part of a form.
I have a complex form structure and the goal is to have a form that specify multiple place where sub components could (or not) inject templates.
Maybe the CDK Portal is not the best solution for this?
I tried something but I am sure it is not the way of doing:
https://stackblitz.com/edit/angular-yuz1kg
I tried also with new ComponentPortal(MyPortalComponent)
but how can we set Inputs on it ? Usually is something like componentRef.component.instance.myInput
You can create a custom injector and inject it to the component portal you create.
createInjector(dataToPass): PortalInjector {
const injectorTokens = new WeakMap();
injectorTokens.set(CONTAINER_DATA, dataToPass);
return new PortalInjector(this._injector, injectorTokens);
}
CONTAINER_DATA
is a custom injector (InjectorToken
) created by -
export const CONTAINER_DATA = new InjectionToken<{}>('CONTAINER_DATA');
To consume created injector, use -
let containerPortal = new ComponentPortal(ComponentToPort, null, this.createInjector({
data1,
data2
}));
overlay.attach(containerPortal);
overlay
is an instance of OverlayRef
(Which is Portal Outlet)
Inside ComponentToPort
, you will need to inject the created injector -
@Inject(CONTAINER_DATA) public componentData: any
More on this here.
Can set component inputs (or bind to outputs as an observable) in this way:
portal = new ComponentPortal(MyComponent);
this.portalHost = new DomPortalHost(
this.elementRef.nativeElement,
this.componentFactoryResolver,
this.appRef,
this.injector
);
const componentRef = this.portalHost.attach(this.portal);
componentRef.instance.myInput = data;
componentRef.instance.myOutput.subscribe(...);
componentRef.changeDetectorRef.detectChanges();
You can inject data to ComponentPortal
with specific injector passed on 3rd param of ComponentPortal
fix syntax issue:
Can't resolve all parameters for Component: ([object Object], [object Object], ?
This is the code
export const PORTAL_DATA = new InjectionToken<{}>('PortalData');
class ContainerComponent {
constructor(private injector: Injector, private overlay: Overlay) {}
attachPortal() {
const componentPortal = new ComponentPortal(
ComponentToPort,
null,
this.createInjector({id: 'first-data'})
);
this.overlay.create().attach(componentPortal);
}
private createInjector(data): PortalInjector {
const injectorTokens = new WeakMap<any, any>([
[PORTAL_DATA, data],
]);
return new PortalInjector(this.injector, injectorTokens);
}
}
class ComponentToPort {
constructor(@Inject(PORTAL_DATA) public data ) {
console.log(data);
}
}