I would like to create an angle component that consists of several sub-components. Eventually I want to create a large reusable tree component whose nodes have different data types. For each data type, the tree component imports sub-components that specify how the node should be displayed. The user should be able to specify a custom component to override the default component for a particular data type. An import syntax like this would be nice:
<app-tree [customDateComp]="CustomDateComponent"></app-tree>
This could be the simplified tree.component.html:
<ng-container *ngIf="!customDateComp">
<app-default-date></app-default-date>
</ng-container>
<ng-container *ngIf="customDateComp">
{{ customDateComp }}
</ng-container>
Of course this does not work yet, because the components are imported with the syntax. The approach below does not work either, because angular escapes it:
<app-tree [customDateComp]="'<app-custom-date></app-custom-date>'"></app-tree>
The sample code can be found here: https://stackblitz.com/edit/angular-ivy-xghj5f?file=src/app/app.component.html
Does anyone have an idea how to import Angular Components into other Components by specifying the component name as an input parameter? Or is there a better way to override default sub-components of a third-party component? Thanks a lot!
Here is your stackblitz demo working.
Among the possible approaches, there is a portal (and the utilities available from @angular/cdk/portal
).
Basically, this is what you need:
1 - A placeholder (in this case, cdkPortalOutlet
setup) for the received component:
<ng-template [cdkPortalOutlet]="_compPortal"></ng-template>
2 - A portal (_compPortal
, above) to plug into your portal outlet:
_compPortal = new ComponentPortal<any>(MyCustomComponent);
That's all.
0 - Import @angular/cdk/portal
module:
import {PortalModule} from '@angular/cdk/portal';
@NgModule({imports: [ ... PortalModule ] ...}) export class AppModule { }
1 - tree.component.html
<ng-container *ngIf="!_compPortal">
<app-date></app-date>
</ng-container>
<ng-container *ngIf="_compPortal">
<ng-template [cdkPortalOutlet]="_compPortal"></ng-template>
</ng-container>
2 - tree.compoenent.ts
@Input()
set customDateComp(customComp: any){
this._compPortal = customComp ? new ComponentPortal(customComp) : null;
}
_compPortal: ComponentPortal<any>;
3 - app.component.ts
// Notice this is not `_comp: CustomDateComponent`.
// I'm setting up an attribute that receives the type so I can
// make it available on the template
_comp = CustomDateComponent;
4 - app.component.html
<app-tree [customDateComp]="_comp"></app-tree>