DEV Community

Jrubzjeknf
Jrubzjeknf

Posted on

Angular: template inside template

You can use a template inside a template to allow a parent component to replace a wrapper around important content.

TL;DR

See this stackblitz.

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.

Example

Here I want to be able to optionally replace the wrapper for part B, while being able to keep part B, like this.

Example with custom wrapper

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>) {}
}
Enter fullscreen mode Exit fullscreen mode

Providing the template using the directive:

<subcomponent>
  <ng-template appWrapper ...>
    ...
  </ng-template>
</subcomponent>
Enter fullscreen mode Exit fullscreen mode

Reading the directive with the template inside the component:

...

export class SubComponent {
  @ContentChild(WrapperDirective, { static: true }) wrapper?: WrapperDirective;
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

And showing the part B template inside the wrapper template:

  <ng-template appWrapper let-partBTemplate="partBTemplate">
    ...

    <ng-container *ngTemplateOutlet="partBTemplate"></ng-container>
  </ng-template>
Enter fullscreen mode Exit fullscreen mode

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)