It is a small article, just to demonstrate to you a trick that can be useful, and its implementation is not so obvious.
Problem
You have a component, that has a child component. For some reason, the child component knows better how to render one thing, and that thing should be actually part of the parent component.
Example 1:
You have a “window” component, that can contain many different components inside, but only they can know what menu items that window should have.
Example 2:
Your parent component is a list, and every child component encapsulates the state for the list item. Depending on the state, different actions are applicable to the item, and the parent should render a specific set of action buttons for every item.
Provided
We can render an <ng-template>
in our child component — that’s an easy part. The only issue is how to “transfer” that template to the parent.
Solution
Angular component decorator has one not-so-popular field, exportAs
.
We can use it to get access to the component instance. And while we have this access, we can use every public
field and method of that component in our template.
@Component({
selector: `x-child`,
standalone: true,
exportAs: 'smartChild',
template: `
<div>Some child's content</div>
<ng-template #menuItems>
<button class="menu-item">Item 1</button>
<button class="menu-item">Item 2</button>
</ng-template>
`
})
export class ChildComponent {
@ViewChild('menuItems') menuItems: TemplateRef<any> | null = null;
}
This component has one public field: menuItems
and one unusual field in its decorator: exportAs: 'smartChild'
.
@Component({
selector: 'x-parent',
standalone: true,
imports: [ChildComponent, NgTemplateOutlet],
template: `
<div class="sophisticated-container">
<div class="menu-can-be-placed-only-as-direct-child">
<ng-container *ngTemplateOutlet="child.menuItems"></ng-container>
</div>
<x-child #child="smartChild"/>
</div>
`
})
export class ParentComponent {}
And that’s how we can use it in a parent:
- we render the child component and assign its component instance to a variable
#child
; - in another part of our parent component, we use
ng-container
(to avoid additional DOM node) and*ngTemplateOutlet
directive to output the template; - we can access every public field of the child component instance, so we can use
child.menuItems
to access the template, rendered by the child component. It is possible because the public fieldmenuItems
of our child component has a decoratorViewChild
pointing to the<ng-template>
in the child components’ template.