Demystifying ng-template: Unraveling Its Power and Use Cases in Angular

Saunak Surani
Widle Studio LLP
Published in
8 min readJul 29, 2023

Angular is a powerful framework for building modern web applications, and it comes with a wide range of features and tools to enhance developer productivity. One such feature is ng-template, which is a versatile and often overlooked tool that can greatly improve the maintainability and performance of your Angular applications.

Unleashing the Potential of ng-template: A Comprehensive Guide with Examples in Angular

In this article, we will explore what ng-template is, its underlying mechanics, and various real-world use cases where it can shine. We will also provide practical code examples to demonstrate how to leverage ng-template effectively in your Angular projects.

Table of Contents

  1. Understanding ng-template
    1.1 What is ng-template?
    1.2 How ng-template Works
  2. Use Cases and Benefits
    2.1 Reusable Content with Template Refs
    2.2 Simplifying Structural Directives
    2.3 Dynamic Rendering and Conditional Logic
    2.4 Performance Optimization with Lazy Loading
  3. Practical Examples
    3.1 Modal Dialogs with ng-template
    3.2 Custom Loading Spinners
    3.3 Tabs and Accordion Components
    3.4 Advanced Data Table with ng-template
  4. Best Practices and Tips
    4.1 Keep Templates Simple and Focused
    4.2 Leverage ng-container for Clean Markup
    4.3 Use Template Outlet to Pass Data
  5. Conclusion

1. Understanding ng-template

1.1 What is ng-template?

ng-template is an Angular directive that allows you to define template content without rendering it immediately. Instead of displaying the content directly on the page, ng-template acts as a placeholder for the content, making it accessible for use later in the application.

The ng-template directive is not limited to a single use case; rather, it can be used in combination with various Angular features to achieve powerful results.

1.2 How ng-template Works

When the Angular application is rendered, ng-template is not directly displayed on the page. Instead, it is compiled by the Angular compiler and kept in the memory as a template definition. Whenever the template is needed, Angular can instantiate it and render its content dynamically.

2. Use Cases and Benefits

2.1 Reusable Content with Template Refs

One of the primary use cases of ng-template is to create reusable content that can be referenced and rendered in multiple places within your application. This is achieved using template refs, which act as placeholders for the ng-template elements.

For example, you can create a loading spinner template using ng-template and use it across different components to display loading indicators.

<!-- spinner-template.html -->
<ng-template #loadingSpinner>
<div class="loading-spinner">
<!-- Your loading spinner content here -->
</div>
</ng-template>
<!-- app.component.html -->
<div>
<h1>Welcome to My App</h1>
<div *ngIf="isLoading; else loadingSpinnerRef">
<!-- Your main content here -->
</div>
<ng-template #loadingSpinnerRef>
<ng-container *ngTemplateOutlet="loadingSpinner"></ng-container>
</ng-template>
</div>

2.2 Simplifying Structural Directives

Angular's structural directives like *ngIf, *ngFor, and *ngSwitch are powerful tools for conditionally rendering elements and iterating over collections. However, when the templates become complex, it can lead to difficult-to-maintain code.

Using ng-template can help simplify complex templates by separating the logic from the markup. You can define the template in ng-template, and then use it with the corresponding structural directive.

<ng-container *ngIf="showContent; else noContent">
<div>
<!-- Your content here -->
</div>
</ng-container>
<ng-template #noContent>
<div>
<p>No content to display.</p>
</div>
</ng-template>

2.3 Dynamic Rendering and Conditional Logic

With ng-template, you can implement dynamic rendering and conditional logic more elegantly. For instance, you can create a modal dialog component that can render different content dynamically based on user interactions.

<!-- modal-dialog.component.html -->
<div class="modal">
<ng-container *ngTemplateOutlet="dialogContent"></ng-container>
</div>
<!-- app.component.html -->
<app-modal-dialog [dialogContent]="dynamicContent"></app-modal-dialog>

<ng-template #dynamicContent>
<div>
<!-- Your dynamic content here -->
</div>
</ng-template>

2.4 Performance Optimization with Lazy Loading

Lazy loading is a common technique to optimize the performance of an Angular application by loading modules on demand. ng-template can be used to improve the lazy loading experience by pre-loading templates for dynamically loaded components.

By using ng-template, the templates for lazy-loaded components can be loaded in advance, reducing the rendering time when the components are eventually displayed.

3. Practical Examples

3.1 Modal Dialogs with ng-template

In this example, we will create a reusable modal dialog component that utilizes ng-template for dynamic content rendering. The modal dialog will display different content based on the data passed to it.

First, let's create the modal dialog component:

// modal-dialog.component.ts

import { Component, Input } from '@angular/core';

@Component({
selector: 'app-modal-dialog',
templateUrl: './modal-dialog.component.html',
styleUrls: ['./modal-dialog.component.css'],
})
export class ModalDialogComponent {
@Input() dialogContent: any;
}

Next, let's define the template for the modal dialog:

<!-- modal-dialog.component.html -->

<div class="modal">
<ng-container *ngTemplateOutlet="dialogContent"></ng-container>
</div>

Now, we can use the modal dialog component in other parts of our application and pass different templates as content:

<!-- app.component.html -->

<app-modal-dialog [dialogContent]="dynamicContent"></app-modal-dialog>

<ng-template #dynamicContent>
<div>
<h2>Dynamic Content Example</h2>
<p>This content is dynamically rendered in the modal dialog.</p>
</div>
</ng-template>

In this example, we define the app-modal-dialog component with an input property dialogContent, which represents the ng-template that will be rendered in the modal dialog. The dynamicContent template is passed to the app-modal-dialog component, and it will be rendered dynamically within the modal.

3.2 Custom Loading Spinners

Loading spinners are a common UI element used to indicate that content is being loaded or processed. With ng-template, we can create a reusable loading spinner and use it across different components.

First, let's define the loading spinner template:

<!-- loading-spinner-template.html -->

<ng-template #loadingSpinner>
<div class="loading-spinner">
<!-- Your loading spinner content here -->
</div>
</ng-template>

Next, we can use the loading spinner in different components:

<!-- app.component.html -->

<div *ngIf="isLoading; else loadingSpinnerRef">
<!-- Your main content here -->
</div>
<ng-template #loading

SpinnerRef>
<ng-container *ngTemplateOutlet="loadingSpinner"></ng-container>
</ng-template>

In this example, we define the loadingSpinner template in a separate file. Then, we use the *ngIf structural directive to conditionally render the main content or the loading spinner based on the isLoading variable. If the content is still loading, the loadingSpinner template will be displayed using ngTemplateOutlet.

3.3 Tabs and Accordion Components

Another powerful use case of ng-template is creating tab and accordion components. These components can render different content in each tab or accordion section, and ng-template provides a clean and efficient way to handle the content.

First, let's create a tab component:

// tab.component.ts

import { Component, Input } from '@angular/core';

@Component({
selector: 'app-tab',
templateUrl: './tab.component.html',
styleUrls: ['./tab.component.css'],
})
export class TabComponent {
@Input() title: string;
}

Next, let's define the template for the tab component:

<!-- tab.component.html -->

<div class="tab">
<ng-content *ngIf="isActive"></ng-content>
</div>

In this template, we use the ng-content directive with the *ngIf structural directive to conditionally render the content of the tab when it is active.

Now, let's create a tabs component that uses the app-tab component:

// tabs.component.ts

import { Component, ContentChildren, QueryList, AfterContentInit } from '@angular/core';
import { TabComponent } from '../tab/tab.component';

@Component({
selector: 'app-tabs',
templateUrl: './tabs.component.html',
styleUrls: ['./tabs.component.css'],
})
export class TabsComponent implements AfterContentInit {
@ContentChildren(TabComponent) tabs: QueryList<TabComponent>;

ngAfterContentInit(): void {
this.tabs.first.isActive = true;
}

activateTab(tab: TabComponent): void {
this.tabs.forEach((t) => (t.isActive = false));
tab.isActive = true;
}
}

In the TabsComponent, we use @ContentChildren to query for app-tab components within the tabs component. When the content is initialized (after ngAfterContentInit), we activate the first tab by default.

The tabs component template looks like this:

<!-- tabs.component.html -->

<div class="tabs">
<ul>
<li *ngFor="let tab of tabs" (click)="activateTab(tab)" [class.active]="tab.isActive">
{{ tab.title }}
</li>
</ul>
<ng-content></ng-content>
</div>

In this template, we display a list of tabs with their titles. When a tab is clicked, the activateTab method is called, and the corresponding content is displayed based on the isActive property of the TabComponent.

With this setup, we can now use the tabs component in our application and provide different content for each tab:

<!-- app.component.html -->

<app-tabs>
<app-tab title="Tab 1">
<div>
<h2>Tab 1 Content</h2>
<p>This is the content for Tab 1.</p>
</div>
</app-tab>
<app-tab title="Tab 2">
<div>
<h2>Tab 2 Content</h2>
<p>This is the content for Tab 2.</p>
</div>
</app-tab>
<app-tab title="Tab 3">
<div>
<h2>Tab 3 Content</h2>
<p>This is the content for Tab 3.</p>
</div>
</app-tab>
</app-tabs>

In this example, we define three tabs with different content, and the tabs component takes care of rendering the active tab content dynamically.

3.4 Advanced Data Table with ng-template

Data tables are a fundamental component in many applications for displaying tabular data. With ng-template, we can create a more advanced and customizable data table that allows you to define the table structure and content separately.

First, let's create a data table component:

// data-table.component.ts

import { Component, ContentChild, TemplateRef } from '@angular/core';

@Component({
selector: 'app-data-table',
templateUrl: './data-table.component.html',
styleUrls: ['./data-table.component.css'],
})
export class DataTableComponent {
@ContentChild('headerTemplate', { static: true }) headerTemplate: TemplateRef<any>;
@ContentChild('rowTemplate', { static: true }) rowTemplate: TemplateRef<any>;
@ContentChild('footerTemplate', { static: true }) footerTemplate: TemplateRef<any>;

tableData: any[]; // Your table data array
}

In the data table component, we use @ContentChild to query for the ng-template elements with the specified template names. These templates represent the header, row, and footer of the data table.

The data table component template looks like this:

<!-- data-table.component.html -->

<table>
<thead>
<tr>
<ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of tableData">
<ng-container *ngTemplateOutlet="rowTemplate; context: { $implicit: item }"></ng-container>
</tr>
</tbody>
<tfoot>
<tr>
<ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
</tr>
</tfoot>
</table>

In this template, we use ng-container to render the header, row, and footer templates dynamically. The ngTemplateOutlet directive is used to include the corresponding templates, and we also use the context property to pass the current item from the data array to the row template.

Now, we can use the data table component and define the templates for header, row, and footer:

<!-- app.component.html -->

<app-data-table [tableData]="data">
<ng-template #headerTemplate>
<th>Name</th>
<th>Age</th>
<th>Email</th>
</ng-template>

<ng-template #rowTemplate let-item>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>{{ item.email }}</td>
</ng-template>

<ng-template #footerTemplate>
<th colspan="3">Total items: {{ data.length }}</th>
</ng-template>
</app-data-table>

In this example, we provide the header, row, and footer templates directly within the data table component. The let-item syntax in the row template allows us to access the current item in the data array and display its properties.

4. Best Practices and Tips

4.1 Keep Templates Simple and Focused

Although ng-template allows for powerful dynamic rendering, it's essential to keep the templates simple and focused. Avoid adding complex logic and excessive markup inside the templates to maintain code readability and ease of maintenance.

4.2 Leverage ng-container for Clean Markup

When using ng-template, the ng-container element is often used as a placeholder for the actual template rendering. This helps keep the markup clean and free from unnecessary elements.

4.3 Use Template Outlet to Pass Data

When using ng-template for dynamic content, you can pass data to the templates using the template outlet's context property. This allows you to access and use the data within the template for rendering.

5. Conclusion

ng-template is a powerful and versatile tool in Angular that enables dynamic content rendering and reusability. By leveraging ng-template, you can create more maintainable, flexible, and efficient Angular applications. Whether it's for reusable components, dynamic rendering, or performance optimization, ng-template offers endless possibilities for enhancing your Angular projects.

In this article, we explored the mechanics of ng-template and its various use cases with practical examples. Armed with this knowledge, you can now confidently apply ng-template in your own projects and take advantage of its capabilities to build more sophisticated and robust Angular applications. Happy coding!

Note: The code examples in this article are for illustrative purposes only and may require additional configuration or modifications depending on your specific project setup.

--

--

Saunak Surani
Widle Studio LLP

Passionate about technology, design, startups, and personal development. Bringing ideas to life at https://widle.studio