5 Strategies for conditional rendering in Angular apps

Roman Akhromieiev
3 min readSep 8, 2023

--

5 Strategies for Conditional Rendering in Angular

Angular Framework provides various concepts to leverage conditional rendering in applications of any size. In this post, I will give 5 short paragraphs about conditional rendering in the Angular framework.

ngIf, ngSwitchCase

ngSwitchCase renders an element based on an expression. It shows ngSwitchDefaultvalue if nothing matches:

<container_element [ngSwitch]="switch_expression">
<inner_element *ngSwitchCase="match_expresson_1">…</inner_element>
<inner_element *ngSwitchCase="match_expresson_2">…</inner_element>
<inner_element *ngSwitchCase="match_expresson_3">…</inner_element>
<inner_element *ngSwitchDefault>…</element>
</container_element>

You need to change the expression and ngSwitch directive will render an element that matches the expression. If you have a lot of elements your html will grow using this directive.

ngSwitchCase is suitable for basic conditional rendering scenarios.

ngTemplate, ngTemplateOutlet

<div class="master">
<ul>
<li *ngFor="let item of items" (click)="selectItem(item)">
{{ item.name }}
</li>
</ul>
</div>
<!-- Detail View -->
<div class="detail">
<ng-container *ngTemplateOutlet="detailTemplate; context: { selectedItem: selectedItem }"></ng-container>
</div>
<ng-template #detailTemplate let-selectedItem="selectedItem">
<div *ngIf="selectedItem">
<h2>{{ selectedItem.name }}</h2>
<p>{{ selectedItem.description }}</p>
</div>
</ng-template>

ngTemplateOutlet allows you to manage more complex scenarios when you need to show different templates with different contexts.

Since Angular v16.2.0 we can dynamically pass inputs to ngTemplateOutlet:

<ng-template *ngComponentOutlet="dynamicComponent; inputs: inputs" />

ngTemplateOutlet can be a killer feature when you need to render recursive components or components based on some nested data. These patterns are characteristically visible in the Angular CDK library,

viewContainerRef.createComponent, viewContainerRef.createEmbeddedView

This approach allows you to load components in runtime dynamically. If you are creating a scalable application with heavy features it’s a MUST to apply. You can split your components into different strategy classes and define how to dynamically load your component in each separate class. The following is one of examples of dynamic loading:

@Component({
selector: 'app-banner',
template: '<ng-container #vcr></ng-container>',
styles: [':host{width: 100%; height: 100%; overflow: auto;}'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [AnimationBannerStrategy, GifBannerStrategy, Html5BannerStrategy, VideoBannerStrategy],
})
export class BannerComponent implements AfterViewInit {
@Input() type = 'html5';
@Input() userData: Record<string, string>;
@ViewChild('vcr', { read: ViewContainerRef }) vcr: ViewContainerRef;
animationBannerStrategy: AnimationBannerStrategy = inject(AnimationBannerStrategy);
html5BannerStrategy: Html5BannerStrategy = inject(Html5BannerStrategy);
videoBannerStrategy: VideoBannerStrategy = inject(VideoBannerStrategy);
gifBannerStrategy: GifBannerStrategy = inject(GifBannerStrategy);

ngAfterViewInit(): void {
this._renderBannerComponent();
}

private _renderBannerComponent(): void {
let strategy: BasicBannerStrategy;
switch (this.type) {
case Banners.Animation:
strategy = this.animationBannerStrategy;
break;
case Banners.Html5:
strategy = this.html5BannerStrategy;
break;
case Banners.Video:
strategy = this.videoBannerStrategy;
break;
case Banners.Gif:
strategy = this.gifBannerStrategy;
break;
default:
strategy = this.defaultBannerStrategy;
break;
}
strategy.renderComponent(this.userData, this.vcr);
}
}

With this approach, you can move all your rendering logic to the component class and make your HTML equal to a single line, only for projection.

Important💡: Incorrect usage can lead to performance issues due to excessive component creation.

Router-outlet

router-outlet directive changes the view based on the route state. In simple terms, you can control the app view using a browser URL.

<div class="master">
<ul>
<li><a routerLink=" /" routerLinkActive="active">Main Detail</a></li>
<li><a routerLink="/item1" routerLinkActive="active">Item 2</a></li>
<li><a routerLink="/item2" routerLinkActive="active">Item 3</a></li>
</ul>
</div>
<div class=detail>
<router-outlet></router-outlet>
</div>

router-outlet is essential for building complex master-detail interfaces with multiple views and routing between them. Since v14 Angular allows to dynamically load components in routing making an impact on loading performance:

export const routes: Routes = [
{
path: 'details',
loadComponent: () =>
import('./details-list/details-list.component').then((x) => x.DetailsListComponent),
},
{
path: 'details/:id',
loadComponent: () =>
import('./details-item/details-item.component').then((x) => x.DetailsItemComponent),
}
];

Cdkportal

CdkPortal is a feature from the angular/cdk package. It is a mix of ngTemplateOutlet and createComponent:

import {
AfterViewInit,
Component,
} from '@angular/core';
import {
ComponentPortal,
PortalModule,
} from '@angular/cdk/portal';

@Component({
selector: 'app-example',
template: '<ng-template [cdkPortalOutlet]="selectedPortal"></ng-template>',
standalone: true,
imports: [PortalModule],
})
export class Example implements AfterViewInit {
componentPortal: ComponentPortal<AnotherPortal>;

ngAfterViewInit() {
this.componentPortal = new ComponentPortal(AnotherPortal);
}
}

CdkPortal provides a mechanism to dynamically render components, TemplateRef instances, and DOM elements. It can be useful when building dynamic popovers/tooltips, dialogs and modals, context menus, popups, and dropdowns. Components from the Angular CDK kit use this directive, so this is one of the building blocks of angular/cdk library.

Conclusion

When working with conditional rendering in Angular applications, you have a variety of strategies at your disposal, each suited for different scenarios.

--

--