Netanel Basal

Learn development with great articles

Simplifying View Logic in Angular

--

Recently, I had to implement a popover view with many variations based on complex conditions. Using ngSwitch, in this case, was impossible, and using ngIf is unreadable and verbose.

Here’s how I decided to approach this problem. The first step was to create a directive named TemplateIdDirective:

@Directive({
selector: 'ng-template[id]',
standalone: true
})
export class TemplateIdDirective {
@Input() id: string;

tpl = inject(TemplateRef);
}

The directive targets templates with an id input. Next, I created the variations in the component template and assigned each an id:

<-- trace-information.component.html -->

<div>
<p>{{ header }}</p>
<ng-template [ngTemplateOutlet]="body"></ng-template>
</div>

<ng-template id="variation-one">...</ng-template>
<ng-template id="variation-two">...</ng-template>
<ng-template id="variation-three">...</ng-template>
<ng-template id="variation-four">...</ng-template>
<ng-template id="variation-five">...</ng-template>

Lastly, I’ve written all the logic in the component, obtained a reference to the templates, and rendered the one that matched:

@Component({
selector: 'app-trace-information',
standalone: true,
imports: [TemplateIdDirective],
templateUrl: './trace-information.component.html'
})
export class TraceInformationComponent {
header = '';

@Input() traceInfo: TraceInfo;

@ViewChildren(TemplateIdDirective)
templates: QueryList<TemplateIdDirective>;

private cdr = inject(ChangeDetectorRef);

ngAfterViewInit() {
this.resolveView();
}

private resolveView() {
// All the complex conditions are defined here.
// In real life is way more complicated than that.
// This is only for illustration
if(...) {
this.header = '...';
this.body = this.getTemplate('variation-one');
} else if(...) {
if(...) {
this.header = '...';
this.body = this.getTemplate('variation-two');
} else {
this.header = '...';
this.body = this.getTemplate('variation-three');
}
}

this.cdr.detectChanges();
}


private getTemplate(id: string) {
return this.templates.find((tpl) => tpl.id === id).tpl;
}
}

Creating a component for every variation would have achieved the same result, but in this case, the view is tiny, so creating a component for every variation would be overkill.

Follow me on Medium or Twitter to read more about Angular and JS!

--

--

Netanel Basal
Netanel Basal

Written by Netanel Basal

A FrontEnd Tech Lead, blogger, and open source maintainer. The founder of ngneat, husband and father.

Responses (6)