You can use a template inside a template to allow a parent component to replace a wrapper around important content.
TL;DR
Why a template inside a template?
In my case, I have a highly reusable component with two important parts, which are tightly coupled. The issue arose when a wrapper around the second part needed to be replaced.
Here I want to be able to optionally replace the wrapper for part B, while being able to keep part B, like this.
Providing a template
For optionally replacing parts of a component, providing a template using a directive and reading it using @ContentChild
is standard procedure.
The directive:
...
@Directive({
selector: '[appWrapper]'
})
export class WrapperDirective {
constructor(public template: TemplateRef<any>) {}
}
Providing the template using the directive:
<subcomponent>
<ng-template appWrapper ...>
...
</ng-template>
</subcomponent>
Reading the directive with the template inside the component:
...
export class SubComponent {
@ContentChild(WrapperDirective, { static: true }) wrapper?: WrapperDirective;
}
And finally rendering the template, using a fallback whenever none is provided:
<ng-container
*ngTemplateOutlet="wrapper?.template || defaultWrapperTemplate">
</ng-container>
<ng-template #defaultWrapperTemplate>
...
</ng-template>
Sweet, step 1 is done. 🚀 Time for the interesting part. 🙂
Providing the template inside the template
A template can be given context. This is an object for passing parameters to your template, which allows it to be more dynamic. For example, a component that shows a list of items could be provided a template to render each item, where the context object is the item's data.
<list>
<ng-template listItem let-item="item">
<div class="{{item.class}}">
{{ item.name }}
</div>
</ng-template>
</list
The cool thing is that a variable within that context object can also be a template. 👍
By passing the part B template to the wrapper template, the wrapper template can show it inside itself where it wants.
Providing the part B template to the wrapper template:
<ng-container
*ngTemplateOutlet="wrapper?.template || defaultWrapperTemplate; context: { partBTemplate: partBTemplate }">
</ng-container>
<ng-template #partBTemplate>
...
</ng-template>
And showing the part B template inside the wrapper template:
<ng-template appWrapper let-partBTemplate="partBTemplate">
...
<ng-container *ngTemplateOutlet="partBTemplate"></ng-container>
</ng-template>
This is all you need! 🎉
By using the template inside a template, you can easily replace a wrapper. It is very powerful and can be provided more context information to make richer wrapping templates based on information inside the component.
To see the full thing in action, see this stackblitz:
Thanks for reading!
Top comments (0)