Content render modes in Vue Tab component

14 Dec 202424 minutes to read

Tabs support rendering content based on different scenarios. The content of the tabs can be rendered in three different ways, as outlined below.

On Demand rendering or lazy loading

This mode is the default, where only the content of the currently selected tab is initially loaded and available in the DOM, with subsequent tab content rendered upon selection. Once a tab’s content is loaded, it remains in the DOM. This ensures that the state of the tabs, such as scroller positions, form values, etc., is preserved.

In the following code example, the Calendar and Scheduler are rendered in the first and second tabs, respectively. Initially, the Scheduler is not available, but it will be rendered once the second tab is selected. Both the Calendar and Scheduler are maintained in the DOM.

<template>
  <div id="app">
    <ejs-tab>
      <e-tabitems>
        <e-tabitem
          :header="headerText0"
          :content="'calendarComponent'"
        ></e-tabitem>
        <template v-slot:calendarComponent>
          <ejs-calendar id="calendar"></ejs-calendar>
        </template>
        <e-tabitem
          :header="headerText1"
          :content="'scheduleComponent'"
        ></e-tabitem>
        <template v-slot:scheduleComponent>
          <ejs-schedule id="Schedule">
            <e-views>
              <e-view option="Day"></e-view>
            </e-views>
          </ejs-schedule>
        </template>
      </e-tabitems>
    </ejs-tab>
  </div>
</template>

<script setup>
import {
  TabComponent as EjsTab,
  TabItemsDirective as ETabitem,
  TabItemDirective as ETabitems,
} from "@syncfusion/ej2-vue-navigations";
import {
  ScheduleComponent as EjsSchedule,
  ViewsDirective as EViews,
  ViewDirective as Eview,
  Day,
} from "@syncfusion/ej2-vue-schedule";
import { CalendarComponent } from "@syncfusion/ej2-vue-calendars";
import { provide } from "vue";

const headerText0 = { text: "Calendar" };
const headerText1 = { text: "Schedule" };
const schedule = [Day];
provide("schedule", schedule);
</script>
<template>
<div id="app">
  <ejs-tab>
    <e-tabitems>
      <e-tabitem :header="headerText0" :content="'calendarComponent'"></e-tabitem>
      <template v-slot:calendarComponent>
      <ejs-calendar id="calendar"></ejs-calendar>
    </template>
      <e-tabitem :header="headerText1" :content="'scheduleComponent'"></e-tabitem>
      <template  v-slot:scheduleComponent>
      <ejs-schedule id="Schedule">
        <e-views>
          <e-view option="Day"></e-view>
        </e-views>
      </ejs-schedule>
    </template>
    </e-tabitems>
  </ejs-tab>
</div>
</template>

<script>
import { TabComponent, TabItemsDirective, TabItemDirective } from "@syncfusion/ej2-vue-navigations";
import { ScheduleComponent, ViewDirective, ViewsDirective, Day} from "@syncfusion/ej2-vue-schedule";
import { CalendarComponent } from "@syncfusion/ej2-vue-calendars";

export default {
name: "App",
components: {
  "ejs-tab": TabComponent,
  "e-tabitem": TabItemDirective,
  "e-tabitems": TabItemsDirective,
  "ejs-schedule": ScheduleComponent,
  "e-view": ViewDirective,
  "e-views": ViewsDirective,
  'ejs-calendar': CalendarComponent,
},
data: function () {
  return {
    headerText0: { text: "Calendar" },
    headerText1: { text: "Schedule" },
  };
},
provide: {
  schedule: [Day],
},
};
</script>

Dynamic rendering

This mode can be applied to Tabs by setting the loadOn property to Dynamic using loadOn. 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 content. Since this mode ensures the DOM maintains only the content of the active tab, page loading performance is improved. However, the Tabs do not retain their state, as each time a tab is selected, it loads its content again.

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 will replace the first tab in the DOM.

<template>
<div id="app">
  <ejs-tab loadOn='Dynamic'>
    <e-tabitems>
      <e-tabitem :header="headerText0" :content="'loginForm'" :disabled="loginDisabled"></e-tabitem>
      <template v-slot:loginForm>
          <div class="login-form">
            <div class="wrap">
              <div id="heading">Sign in to view the Grid</div>
              <br />
              <div id="input-container">
              <ejs-textbox ref="userObj" floatLabelType="Auto" :value="userName" placeholder="Username"></ejs-textbox>
              <br /><br />
              <ejs-textbox ref="passwordObj" floatLabelType="Auto" :value="passWord" placeholder="Password"></ejs-textbox>
              </div>
            </div>
            <br />
            <div class="button-container">
              <ejs-button :isPrimary="true" v-on:click="handleSubmit">Sign In</ejs-button>
            </div>
          </div>
      </template>

      <e-tabitem :header="headerText1" :content="'grid'" :disabled="gridDisabled"></e-tabitem>
        <template v-slot:grid>
          <ejs-grid ref="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>
        </template>
    </e-tabitems>
  </ejs-tab>
</div>
</template>
<script setup>
import { TabComponent as EjsTab, TabItemsDirective as ETabitem, TabItemDirective as ETabitems } from "@syncfusion/ej2-vue-navigations";
import { TextBoxComponent as EjsTextbox  } from "@syncfusion/ej2-vue-inputs";
import { ButtonComponent as EjsButton  } from "@syncfusion/ej2-vue-buttons";
import { GridComponent as EjsGrid, ColumnsDirective as EColumns, ColumnDirective as EColumn} from "@syncfusion/ej2-vue-grids";
import { ref } from "vue";

const userObj = ref(null);
const passwordObj = ref(null);

const headerText0 = { text: "Login" };
const headerText1 = { text: "Grid" };
const loginDisabled = false;
const gridDisabled =  true;
const userName = "";
const passWord = "";
const gridData = [
        { 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 } ]

const handleSubmit = () => {
      const userName = userObj.value.$el.ej2_instances[0].value;
      const password = passwordObj.value.$el.ej2_instances[0].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 {
        userObj.value.$el.ej2_instances[0].value = "";
        passwordObj.value.$el.ej2_instances[0].value = "";
        this.loginDisabled = true;
        this.gridDisabled = false
      }
}
</script>

<style>
   .button-container {
      display: flex;
      justify-content: center;
   }
</style>
<template>
<div id="app">
  <ejs-tab loadOn='Dynamic'>
    <e-tabitems>
      <e-tabitem :header="headerText0" :content="'loginForm'" :disabled="loginDisabled"></e-tabitem>
      <template v-slot:loginForm>
          <div class="login-form">
            <div class="wrap">
              <div id="heading">Sign in to view the Grid</div>
              <br />
              <div id="input-container">
              <ejs-textbox ref="userObj" floatLabelType="Auto" :value="userName" placeholder="Username"></ejs-textbox>
              <br /><br />
              <ejs-textbox ref="passwordObj" floatLabelType="Auto" :value="passWord" placeholder="Password"></ejs-textbox>
              </div>
            </div>
            <br />
            <div class="button-container">
              <ejs-button :isPrimary="true" @click="handleSubmit">Sign In</ejs-button>
            </div>
          </div>
      </template>

      <e-tabitem :header="headerText1" :content="'grid'" :disabled="gridDisabled"></e-tabitem>
        <template v-slot:grid>
          <ejs-grid ref="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>
        </template>
    </e-tabitems>
  </ejs-tab>
</div>
</template>
<script>
import { TabComponent, TabItemsDirective, TabItemDirective } from "@syncfusion/ej2-vue-navigations";
import { TextBoxComponent } from "@syncfusion/ej2-vue-inputs";
import { ButtonComponent } from "@syncfusion/ej2-vue-buttons";
import { GridComponent, ColumnsDirective, ColumnDirective } from "@syncfusion/ej2-vue-grids";

export default {
name: "App",
components: {
  "ejs-tab": TabComponent,
  "e-tabitem": TabItemDirective,
  "e-tabitems": TabItemsDirective,
  "ejs-textbox": TextBoxComponent,
  "ejs-button": ButtonComponent,
  "ejs-grid": GridComponent,
  "e-columns": ColumnsDirective,
  "e-column": ColumnDirective,
},
data: function () {
  return {
    headerText0: { text: "Login" },
    headerText1: { text: "Grid" },
    loginDisabled: false,
    gridDisabled: true,
    userName: "",
    passWord: "",
    gridData: [
      { 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 } ]
  };
},
methods: {
    handleSubmit() {
      const userName = this.$refs.userObj.ej2Instances.value;
      const password = this.$refs.passwordObj.ej2Instances.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.$refs.userObj.ej2Instances.value = "";
        this.$refs.passwordObj.ej2Instances.value = "";
        this.loginDisabled = true;
        this.gridDisabled = false
      }
    },
}
};
</script>

<style>
   .button-container {
      display: flex;
      justify-content: center;
   }
</style>

On initial rendering

This mode can be applied to Tabs by setting the loadOn property to Init using loadOn. In this mode, the content of all the 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.

<template>
<div id="app">
  <ejs-tab :selectedItem="selectedItem" loadOn="Init">
    <e-tabitems>
      <e-tabitem :header="headerText0" :content="'loginForm'"></e-tabitem>
      <template v-slot:loginForm>
          <div class="login-form">
            <div class="wrap">
              <div id="input-container">
              <ejs-textbox ref="userObj" floatLabelType="Auto" :value="userName" placeholder="Username"></ejs-textbox>
              <br /><br />
              <ejs-textbox ref="passwordObj" floatLabelType="Auto" :value="passWord" placeholder="Password"></ejs-textbox>
              </div>
            </div>
            <br />
            <div class="button-container">
              <ejs-button :isPrimary="true" v-on:click="handleSignIn">Sign In</ejs-button>
              <ejs-button :isPrimary="true" v-on:click="handleSkip">Skip In</ejs-button>
            </div>
          </div>
      </template>

      <e-tabitem :header="headerText1" :content="'overView'" ></e-tabitem>
        <template v-slot:overView>
        <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>
        </template>
        <e-tabitem :header="headerText2" :content="'feedBack'" ></e-tabitem>
        <template v-slot:feedBack>
          <div class="feed-back">
          <div class="wrap">
            <div id="input-container">
            <ejs-textbox floatLabelType="Auto" ref="nameObj" :value="userName" placeholder="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">
           <ejs-button :isPrimary="true" v-on:click="handleSubmit">Submit</ejs-button>
          </div>
        </div>
        </template>
    </e-tabitems>

  </ejs-tab>
</div>
</template>

<script setup>
import { TabComponent as EjsTab, TabItemsDirective as ETabitem, TabItemDirective as ETabitems } from "@syncfusion/ej2-vue-navigations";
import { TextBoxComponent as EjsTextbox  } from "@syncfusion/ej2-vue-inputs";
import { ButtonComponent as EjsButton  } from "@syncfusion/ej2-vue-buttons";

import { ref } from "vue";

const userObj = ref(null);
const passwordObj = ref(null);
const nameObj = ref(null);

const headerText0 = { text: "Login" };
const headerText1 = { text: "Syncfusion EJ2" };
const headerText2 = { text: "FeedBack" };
const userName = "";
const passWord = "";
let selectedItem = 0;


const handleSignIn = () => {
    const userName = userObj.value.$el.ej2_instances[0].value;
    const password = passwordObj.value.$el.ej2_instances[0].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");
    }
    nameObj.value$el.ej2_instances[0].value = userName;
    this.selectedItem = 1;
}

const handleSkip = () => {
    this.selectedItem = 1;
}

const handleSubmit =  () =>  {
    userObj.value.$el.ej2_instances[0].value = '';
    passwordObj.value.$el.ej2_instances[0].value = ''
    this.selectedItem = 0;
}
</script>

<style>
.button-container {
    display: flex;
    justify-content: center;
    align-items: center; 
    gap: 10px;
}
</style>
<template>
<div id="app">
  <ejs-tab :selectedItem="selectedItem" loadOn="Init">
    <e-tabitems>
      <e-tabitem :header="headerText0" :content="'loginForm'"></e-tabitem>
      <template v-slot:loginForm>
          <div class="login-form">
            <div class="wrap">
              <div id="input-container">
              <ejs-textbox ref="userObj" floatLabelType="Auto" :value="userName" placeholder="Username"></ejs-textbox>
              <br /><br />
              <ejs-textbox ref="passwordObj" floatLabelType="Auto" :value="passWord" placeholder="Password"></ejs-textbox>
              </div>
            </div>
            <br />
            <div class="button-container">
              <ejs-button :isPrimary="true" v-on:click="handleSignIn">Sign In</ejs-button>
              <ejs-button :isPrimary="true" v-on:click="handleSkip">Skip In</ejs-button>
            </div>
          </div>
      </template>

      <e-tabitem :header="headerText1" :content="'overView'" ></e-tabitem>
        <template v-slot:overView>
        <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>
        </template>
        <e-tabitem :header="headerText2" :content="'feedBack'" ></e-tabitem>
        <template v-slot:feedBack>
          <div class="feed-back">
          <div class="wrap">
            <div id="input-container">
            <ejs-textbox floatLabelType="Auto" ref="nameObj" :value="userName" placeholder="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">
           <ejs-button :isPrimary="true" v-on:click="handleSubmit">Submit</ejs-button>
          </div>
        </div>
        </template>
    </e-tabitems>

  </ejs-tab>
</div>
</template>
<script>
import { TabComponent, TabItemsDirective, TabItemDirective } from "@syncfusion/ej2-vue-navigations";
import { TextBoxComponent } from "@syncfusion/ej2-vue-inputs";
import { ButtonComponent } from "@syncfusion/ej2-vue-buttons";

export default {
name: "App",
components: {
  "ejs-tab": TabComponent,
  "e-tabitem": TabItemDirective,
  "e-tabitems": TabItemsDirective,
  "ejs-textbox": TextBoxComponent,
  "ejs-button": ButtonComponent
},
data: function () {
  return {
    headerText0: { text: "Login" },
    headerText1: { text: "Syncfusion EJ2" },
    headerText2: { text: "FeedBack" },
    userName: "",
    passWord: "",
    selectedItem: 0
  };
},
methods: {
    handleSignIn() {
      const userName = this.$refs.userObj.ej2Instances.value;
      const password = this.$refs.passwordObj.ej2Instances.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");
      }
      this.$refs.nameObj.ej2Instances.value = userName;
      this.selectedItem = 1;
    },
    handleSkip() {
        this.selectedItem = 1;
    },
    handleSubmit () {
        this.$refs.userObj.ej2Instances.value = '';
        this.$refs.passwordObj.ej2Instances.value = ''
        this.selectedItem = 0;
    }
},
};
</script>
<style>
.button-container {
    display: flex;
    justify-content: center;
    align-items: center; 
    gap: 10px;
}
</style>