I am trying to improve my code rather than having conditions, so I decided to create a directive or maybe a pipe if possible that could help me print the title of a tab depending on its type (string or templateRef), my code is the following, this code is used in my Tabs/Tab component, but also in my Stepper/step component, so I believe it would be great to create something reusable. I've tried doing it with ElementRef, Renderer2, ViewContainerRef, TemplateRef... but I was unsuccessful.
<ng-container *ngIf="tab.isLabelTemplate">
<ng-container *ngTemplateOutlet="tab.title">
</ng-container>
</ng-container>
<ng-container *ngIf="!tab.isLabelTemplate">{{ tab.title }}</ng-container>
The isLabelTemplate looks like this:
get isLabelTemplate(): boolean {
return this.title instanceof TemplateRef;
}
Thanks a lot :)
You can leverage Angular low-level API to dynamically manipulate DOM's structure:
ViewContainerRef::createEmbeddedView to render TemplateRef near the directive's host element
Renderer2::insertBefore to render TextNode near the directive's host element
Here's an example of how that directive can look like:
title-template.directive.ts
import { Directive, TemplateRef, Renderer2, ViewContainerRef, Input } from '@angular/core';
@Directive({
selector: '[titleTemplate]',
})
export class TitleTemplateDirective {
@Input()
set titleTemplate(value: string | TemplateRef<any>) {
this.updateView(value);
}
textNode: Text;
constructor(private vcRef: ViewContainerRef, private renderer: Renderer2) {}
private updateView(value: string | TemplateRef<any>) {
this.clear();
if (!value) {
return;
}
if (value instanceof TemplateRef) {
this.vcRef.createEmbeddedView(value);
} else {
this.textNode = this.renderer.createText(value);
const elem = this.vcRef.element.nativeElement;
this.renderer.insertBefore(elem.parentNode, this.textNode, elem);
}
}
private clear() {
this.vcRef.clear();
if (this.textNode) {
this.renderer.removeChild(this.textNode.parentNode, this.textNode);
}
}
ngOnDestroy() {
this.clear();
}
}
Usage:
<ng-container [titleTemplate]="title"></ng-container>