Content render modes in Angular Tab component
22 Sep 202524 minutes to read
The Tab component supports rendering content based on different scenarios to optimize performance and user experience. The content of tabs can be rendered in three different ways, as outlined below.
On Demand rendering or lazy loading
This mode is the default rendering behavior, where only the content of the currently selected tab is initially loaded and available in the DOM. Subsequent tab content is rendered upon selection. Once a tab’s content is loaded, it remains in the DOM, ensuring that the state of tabs (such as scroll positions, form values, and component states) is preserved.
This approach provides a balance between performance and state management, making it suitable for most applications where maintaining tab state is important.
In the following code example, the Calendar and Scheduler components are rendered in the first and second tabs, respectively. Initially, the Scheduler is not available in the DOM, but it will be rendered once the second tab is selected. Both the Calendar and Scheduler are maintained in the DOM after their initial rendering.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { Component, ViewChild } from '@angular/core';
import { TabModule, TabComponent } from '@syncfusion/ej2-angular-navigations'
import { DayService, ScheduleModule } from '@syncfusion/ej2-angular-schedule';
import { CalendarModule } from '@syncfusion/ej2-angular-calendars';
@Component({
imports: [ FormsModule, TabModule, ScheduleModule, CalendarModule ],
providers: [DayService],
standalone: true,
selector: 'app-container',
template: `
<div class="control-section e-tab-section">
<div class="e-sample-resize-container">
<!-- Render the Tab Component -->
<ejs-tab id="tab_default">
<e-tabitems>
<e-tabitem [header]="headerText[0]">
<ng-template #content>
<ejs-calendar></ejs-calendar>
</ng-template>
</e-tabitem>
<e-tabitem [header]="headerText[1]">
<ng-template #content>
<ejs-schedule #scheduleObj width="100%" height="650px">
<e-views>
<e-view option="Day"></e-view>
</e-views>
</ejs-schedule>
</ng-template>
</e-tabitem>
</e-tabitems>
</ejs-tab>
</div>
</div>
`
})
export class AppComponent {
public headerText: Object[] = [{ text: "Calendar" },{ text: "Schedule" }];
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Dynamic rendering
This mode can be applied to the Tab component by setting the loadOn property to Dynamic. In this mode, only the content of the currently selected tab is loaded and available in the DOM initially. When a different tab is selected, its content replaces the current tab’s content in the DOM. Since this mode ensures the DOM maintains only the content of the active tab, page loading performance is significantly improved. However, tabs do not retain their state, as each time a tab is selected, it loads its content from scratch.
This rendering mode is ideal for applications with many tabs or heavy content where memory optimization is crucial, and state preservation is not a primary concern.
In the following code example, there are two tabs. The first tab contains a login page, and the second tab contains a Grid component. The Grid component in the second tab will only be rendered in the DOM after the login is completed. Upon successful login, the second tab content will replace the first tab content in the DOM.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { Component, ViewChild } from '@angular/core';
import { TabModule, TabComponent } from '@syncfusion/ej2-angular-navigations';
import { TextBoxModule, TextBoxComponent } from '@syncfusion/ej2-angular-inputs';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { GridModule } from '@syncfusion/ej2-angular-grids';
/**
* Adaptive Tab Component
*/
@Component({
imports: [ FormsModule, TabModule, TextBoxModule, ButtonModule, GridModule],
standalone: true,
selector: 'app-container',
// specifies the template url path
template: ` <<div class="control-section e-tab-section">
<div class="e-sample-resize-container">
<!-- Render the Tab Component -->
<ejs-tab #tabObj loadOn="Dynamic">
<e-tabitems>
<e-tabitem [header]="headerText[0]" [disabled]="loginDisabled">
<ng-template #content>
<div class="login-form">
<div class="wrap">
<div id="heading">Sign in to view the Grid</div>
<br />
<div id="input-container">
<ejs-textbox #userObj floatLabelType="Auto" placeholder="Username"></ejs-textbox>
<br /><br />
<ejs-textbox #passwordObj floatLabelType="Auto" placeholder="Password"></ejs-textbox>
</div>
</div>
<br />
<div class="button-container">
<button ejs-button [isPrimary]="true" (click)="handleSubmit()">sign In</button>
</div>
</div>
</ng-template>
</e-tabitem>
<e-tabitem [header]="headerText[1]" [disabled]="gridDisabled">
<ng-template #content>
<ejs-grid id='overviewgrid' [dataSource]='gridData'>
<e-columns>
<e-column field="OrderID" headerText="Order ID" width="120" ></e-column>
<e-column field="CustomerID" headerText="Customer Name" width="130" ></e-column>
<e-column field="OrderDate" headerText="Order Date" width="120" format="yMd"></e-column>
<e-column field="Freight" headerText="Freight" width="120" format="C2"></e-column>
</e-columns>
</ejs-grid>
</ng-template>
</e-tabitem>
</e-tabitems>
</ejs-tab>
</div>
</div>`
})
export class AppComponent {
@ViewChild('tabObj') public tabObj!: TabComponent;
@ViewChild('userObj') public userObj!: TextBoxComponent;
@ViewChild('passwordObj') public passwordObj!: TextBoxComponent;
public loginDisabled: Boolean = false;
public gridDisabled: Boolean = true;
public headerText: Object [] = [{ text: "Login" }, { text: "Grid" }];
public gridData: object [] = [
{ OrderID: 10248, CustomerID: "ALFKI", OrderDate: "2024-12-01", Freight: 32.38 },
{ OrderID: 10249, CustomerID: "ANATR", OrderDate: "2024-12-02", Freight: 11.61 },
{ OrderID: 10250, CustomerID: "ANTON", OrderDate: "2024-12-03", Freight: 65.83 },
{ OrderID: 10251, CustomerID: "AROUT", OrderDate: "2024-12-04", Freight: 41.34 }
];
public handleSubmit(): void {
const userName = this.userObj.value;
const password = this.passwordObj.value;
if (!userName && !password) {
window.alert("Enter both username and password");
} else if (!userName) {
window.alert("Enter the username");
} else if (!password) {
window.alert("Enter the password");
} else if (userName.length < 4) {
window.alert("Username must be at least 4 characters long");
} else {
this.userObj.value = "";
this.passwordObj.value = "";
this.tabObj.items[0].disabled = true;
this.tabObj.items[1].disabled = false;
this.tabObj.dataBind();
this.tabObj.select(1);
}
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));On initial rendering
This mode can be applied to the Tab component by setting the loadOn property to Init. In this mode, the content of all tabs is rendered on initial load and maintained in the DOM. This mode is ideal when you have a small number of tabs and need to preserve the state of each tab. It also allows you to access the references of components rendered in other tabs.
In the following example, all three tabs are rendered on the initial load, and the data entered in the first tab will be maintained even when the second or third tab is active.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { Component, ViewChild } from '@angular/core';
import { TabModule, TabComponent } from '@syncfusion/ej2-angular-navigations';
import { TextBoxModule, TextBoxComponent } from '@syncfusion/ej2-angular-inputs';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
/**
* Adaptive Tab Component
*/
@Component({
imports: [ FormsModule, TabModule, TextBoxModule, ButtonModule],
standalone: true,
selector: 'app-container',
// specifies the template url path
template: ` <div class="control-section e-tab-section">
<div class="e-sample-resize-container">
<!-- Render the Tab Component -->
<ejs-tab #tabObj loadOn="Init" [selectedItem]="selectedItem">
<e-tabitems>
<e-tabitem [header]="headerText[0]">
<ng-template #content>
<div class="login-form">
<div class="wrap">
<div id="input-container">
<ejs-textbox #userObj floatLabelType="Auto" placeholder="Username"></ejs-textbox>
<br /><br />
<ejs-textbox #passwordObj floatLabelType="Auto" placeholder="Password"></ejs-textbox>
</div>
</div>
<br />
<div class="button-container">
<button ejs-button [isPrimary]="true" (click)="handleSignIn()">Sign In</button>
<button ejs-button [isPrimary]="true" (click)="handleSkip()">Skip In</button>
</div>
</div>
</ng-template>
</e-tabitem>
<e-tabitem [header]="headerText[1]">
<ng-template #content>
<div class="over-view">
<p>
You can check out our Syncfusion Ej2 demo
<a href="https://ej2.syncfusion.com/demos/" target="_blank" rel="noopener noreferrer">
here
</a>.
</p>
<br />
<p>
The user guide is available
<a href="https://ej2.syncfusion.com/documentation/introduction" target="_blank" rel="noopener noreferrer">
here
</a>.
</p>
</div>
</ng-template>
</e-tabitem>
<e-tabitem [header]="headerText[2]">
<ng-template #content>
<div class="feed-back">
<div class="wrap">
<div id="input-container">
<ejs-textbox floatLabelType="Auto" #nameObj placeholder="User Name"></ejs-textbox>
<br /><br />
<ejs-textbox floatLabelType="Auto" placeholder="Email"></ejs-textbox>
<br /><br />
<ejs-textbox floatLabelType="Auto" placeholder="Comments"></ejs-textbox>
</div>
</div>
<br />
<div class="button-container">
<button ejs-button :isPrimary="true" (click)="handleSubmit()">Submit</button>
</div>
</div>
</ng-template>
</e-tabitem>
</e-tabitems>
</ejs-tab>
</div>
</div>`
})
export class AppComponent {
@ViewChild('tabObj') public tabObj!: TabComponent;
@ViewChild('userObj') public userObj!: TextBoxComponent;
@ViewChild('passwordObj') public passwordObj!: TextBoxComponent;
@ViewChild('nameObj') public nameObj!: TextBoxComponent;
public selectedItem: number = 0;
public headerText: Object[] = [{ text: "Login" }, { text: "Syncfusion EJ2" }, { text: "FeedBack" }];
public handleSignIn(): void {
const userName = this.userObj.value;
const password = this.passwordObj.value;
if (!userName && !password) {
window.alert("Enter both username and password");
} else if (!userName) {
window.alert("Enter the username");
} else if (!password) {
window.alert("Enter the password");
} else if (userName.length < 4) {
window.alert("Username must be at least 4 characters long");
} else {
this.selectedItem = 1;
}
this.nameObj.value = userName
}
public handleSkip = () => {
this.selectedItem = 1;
}
public handleSubmit = () => {
this.userObj.value = '';
this.passwordObj.value = '';
this.nameObj.value = '';
this.selectedItem = 0;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));