Templates in Syncfusion® Vue Components
25 Jun 202624 minutes to read
Syncfusion® Vue components render with a predefined layout or structure that defines how the component appears in the UI. When you need to customize appearance or add application-specific functionality, Syncfusion® Vue components provide template support to achieve this.
Types of templates
Syncfusion® Vue components support three types of templates:
Slot template
The Syncfusion® Vue components do support slots, which can help reduce the number of properties that need to be defined and increase the readability of the component. This is because using slots allows defining the content or behaviour of the component in the parent component rather than in the component’s own code. This can make it easier to understand the purpose and functionality of the component at a glance and make the component more modular and flexible.
In a Vue component, use the v-slot directive to define a slot template where users can insert custom content. See the sample below.
<ejs-grid ref="grid" :dataSource="ds">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width=120 textAlign="Right" />
<e-column field="CustomerName" headerText="Customer Name" width=150 />
<e-column field="ShipCountry" headerText="Ship Country" width=150 :template="'cTemplate'">
<template v-slot:cTemplate>
<ejs-button :content="ShipCountry"></ejs-button>
</template>
</e-column>
</e-columns>
</ejs-grid>Render scope
Slot content has access to the parent component scope. To access a component’s data source value inside the template, accept props (for example { data }) in the v-slot directive. Expressions within the slot can then access the component’s data.
<template v-slot:templateName="{data}">
<ejs-button :content="data.ShipCountry"></ejs-button>
</template>Named slot
The Syncfusion® Vue components support multiple templates. Each template is differentiated by its name. To render the slot content to the corresponding slot outlet, the name of each slot must map to the name of the corresponding property.
<template v-slot:templateName></template>When passing a slot to a component, ensure that the component’s property value is of the “string” type.
An example of a Grid component sample with a named slot (cTemplate) template is shown below.
<ejs-grid ref="grid" :dataSource="ds">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width=120 textAlign="Right" />
<e-column field="CustomerName" headerText="Customer Name" width=150 />
<e-column field="ShipCountry" headerText="Ship Country" width=150 :template="'cTemplate'"/>
</e-columns>
<template v-slot:cTemplate="{data}">
<ejs-button :content="data.ShipCountry"></ejs-button>
</template>
</ejs-grid>The slot template can also be used to insert content into nested tags within a component. In the below code example, cTemplate is rendered in the nested tag <e-column>.
<template>
<div id="grid">
<ejs-grid :dataSource="ds">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width=120 textAlign="Right" />
<e-column field="CustomerName" headerText="Customer Name" width=150 />
<e-column field="ShipCountry" headerText="Ship Country" width=150 :template="'cTemplate'">
<template v-slot:cTemplate="{ data }">
<ejs-button :content="data.ShipCountry"></ejs-button>
</template>
</e-column>
</e-columns>
</ejs-grid>
</div>
</template>
<script setup>
import { GridComponent as EjsGrid, ColumnsDirective as EColumns, ColumnDirective as EColumn } from '@syncfusion/ej2-vue-grids';
import { ButtonComponent as EjsButton } from "@syncfusion/ej2-vue-buttons";
const ds = [
{ OrderID: 10248, ShipCountry: "France", CustomerName: "Paul Henriot" },
{ OrderID: 10249, ShipCountry: "Germany", CustomerName: "Karin Josephs" },
{ OrderID: 10250, ShipCountry: "Brazil", CustomerName: "Mario Pontes" },
{ OrderID: 10251, ShipCountry: "France", CustomerName: "Mary Saveley" }
];
const data = () => { return { ds: empData }; }
</script>
<style>
@import "../node_modules/@syncfusion/ej2-material3-theme/styles/grid/index.css";
</style><template>
<div id="grid">
<ejs-grid ref="grid" :dataSource="ds">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width=120 textAlign="Right" />
<e-column field="CustomerName" headerText="Customer Name" width=150 />
<e-column field="ShipCountry" headerText="Ship Country" width=150 :template="'cTemplate'">
<template v-slot:cTemplate="{ data }">
<ejs-button :content="data.ShipCountry"></ejs-button>
</template>
</e-column>
</e-columns>
</ejs-grid>
</div>
</template>
<script>
import { ButtonComponent } from '@syncfusion/ej2-vue-buttons';
import { GridComponent, ColumnsDirective, ColumnDirective } from "@syncfusion/ej2-vue-grids";
var empData = [
{ OrderID: 10248, ShipCountry: "France", CustomerName: "Paul Henriot" },
{ OrderID: 10249, ShipCountry: "Germany", CustomerName: "Karin Josephs" },
{ OrderID: 10250, ShipCountry: "Brazil", CustomerName: "Mario Pontes" },
{ OrderID: 10251, ShipCountry: "France", CustomerName: "Mary Saveley" }
];
export default {
name: "App",
components: {
"ejs-grid": GridComponent,
"e-columns": ColumnsDirective,
"e-column": ColumnDirective,
"ejs-button": ButtonComponent
},
data() { return { ds: empData } }
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material.css";
</style>Inline template
The user can use the app.component method to add custom content to the template that can be used in the Syncfusion® Vue components. The template elements can be added to template attribute of the app.component method. Refer to the below code snippet to create the template element using app.component method.
import { createApp } from "vue/dist/vue.esm-bundler.js";
const app = createApp();
const inlineTemplate = app.component('inlineTemplate', {
components: {
'ejs-button': ButtonComponent
},
data: () => ({}),
template: '<ejs-button :content="`${data.ShipCountry}`"></ejs-button>'
});Create a template function that returns an object { key: 'template', value: 'importedTemplate' } to map this template to the Grid component.
const cTemplate = () => {
return { template: inlineTemplate };
}Now, the template function is assigned to the template property of the Grid component. Refer to the below example for the inline template.
<template>
<div id="grid">
<ejs-grid ref="grid" :dataSource="ds">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width=120 textAlign="Right" />
<e-column field="CustomerName" headerText="Customer Name" width=150 />
<e-column field="ShipCountry" headerText="Ship Country" width=150 :template='cTemplate' />
</e-columns>
</ejs-grid>
</div>
</template>
<script setup>
import { GridComponent as EjsGrid, ColumnsDirective as EColumns, ColumnDirective as EColumn } from '@syncfusion/ej2-vue-grids';
import { ButtonComponent } from "@syncfusion/ej2-vue-buttons";
import { createApp } from "vue";
const app = createApp();
const inlineTemplate = app.component('inlineTemplate', {
components: {
'ejs-button': ButtonComponent
},
data: () => ({}),
template: '<ejs-button :content="`${data.ShipCountry}`"></ejs-button>'
});
const cTemplate = () => {
return { template: inlineTemplate };
}
const ds = [
{ OrderID: 10248, ShipCountry: "France", CustomerName: "Paul Henriot" },
{ OrderID: 10249, ShipCountry: "Germany", CustomerName: "Karin Josephs" },
{ OrderID: 10250, ShipCountry: "Brazil", CustomerName: "Mario Pontes" },
{ OrderID: 10251, ShipCountry: "France", CustomerName: "Mary Saveley" }
];
</script>
<style>
@import "../node_modules/@syncfusion/ej2-material3-theme/styles/grid/index.css";
</style><script>
import { createApp } from "vue";
import { ButtonComponent } from '@syncfusion/ej2-vue-buttons';
import { GridComponent, ColumnsDirective, ColumnDirective } from "@syncfusion/ej2-vue-grids";
var app = createApp();
var empData = [
{ OrderID: 10248, ShipCountry: "France", CustomerName: "Paul Henriot" },
{ OrderID: 10249, ShipCountry: "Germany", CustomerName: "Karin Josephs" },
{ OrderID: 10250, ShipCountry: "Brazil", CustomerName: "Mario Pontes" },
{ OrderID: 10251, ShipCountry: "France", CustomerName: "Mary Saveley" }
];
export default {
name: "App",
components: {
"ejs-grid":GridComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective,
"ejs-button":ButtonComponent
},
data () {
return {
ds: empData,
cTemplate: function () {
return { template: app.component("inlineTemplate", {
template: '<ejs-button :content="`${data.ShipCountry}`"></ejs-button>',
data() { return { data: {} }; }
}) };
}
}
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-material3-theme/styles/grid/index.css";
</style>External template
The template elements can be defined in an external file (single-file component) and used in Syncfusion® Vue components. Refer to the below code snippet to define template elements in template.vue file.
<template>
<div class="button">
<ejs-button :content="`${data.ShipCountry}`"> </ejs-button>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
import { ButtonComponent as EjsButton } from '@syncfusion/ej2-vue-buttons';
const data = defineProps(['ShipCountry']);
</script>Import the template.vue file into the corresponding App.vue file as specified in the following code snippet.
import template from './template.vue';Create a template function that returns an object { key: 'template', value: 'importedTemplate' } to map this template to the Grid component.
const app = createApp();
const externalTemplate = app.component('externalTemplate', template);
const cTemplate = () => {
return { template: externalTemplate };
}Now, the template function is assigned to the template property of the Grid component. Refer to the below code snippet from App.vue file for the external template.
<template>
<div id="app">
<ejs-grid ref="grid" :dataSource="ds">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width=120 textAlign="Right" />
<e-column field="CustomerName" headerText="Customer Name" width=150 />
<e-column field="ShipCountry" headerText="Ship Country" width=150 :template='cTemplate'/>
</e-columns>
</ejs-grid>
</div>
</template>
<script setup>
import { createApp } from "vue";
import { GridComponent as EjsGrid, ColumnsDirective as EColumns, ColumnDirective as EColumn } from "@syncfusion/ej2-vue-grids";
import { ButtonComponent } from "@syncfusion/ej2-vue-buttons";
import template from "./template.vue";
const app = createApp();
const externalTemplate = app.component('externalTemplate', template);
const cTemplate = () => {
return { template: externalTemplate };
}
const ds = [
{ OrderID: 10248, ShipCountry: "France", CustomerName: "Paul Henriot" },
{ OrderID: 10249, ShipCountry: "Germany", CustomerName: "Karin Josephs" },
{ OrderID: 10250, ShipCountry: "Brazil", CustomerName: "Mario Pontes" },
{ OrderID: 10251, ShipCountry: "France", CustomerName: "Mary Saveley" }
];
</script>
<style>
@import "../node_modules/@syncfusion/ej2-material3-theme/styles/grid/index.css";
</style>External modules in templates
Syncfusion® provides the option to use external modules in template content. To use the external modules in the template, add those modules to the plugins property of the Vue component. For example, the “i18n” module is added to the plugins property of the Grid component. Refer to the below code snippet.
<template>
<h3>Grid component</h3>
<ejs-grid height='210px' :plugins="modules"></ejs-grid>
</template>
<script setup>
import { i18n } from "./main";
import { GridComponent as EjsGrid } from '@syncfusion/ej2-vue-grids';
const modules= [i18n]
</script>Below is the example code to define i18n external module in the Vue 3 application.
import { createApp } from 'vue'
import App from './App.vue'
import { createI18n } from "vue-i18n";
const messages = {
en: {
message: {
customer: "Customer Name",
},
},
ja: {
message: {
customer: "顧客名",
},
},
};
export const i18n = createI18n({
legacy: false,
locale: "ja",
fallbackLocale: "en",
messages,
});
createApp(App).use(i18n).mount('#app')Below is the example code to use i18n external module in the Grid component template using plugins property.
<template>
<ejs-grid :dataSource="empData" :plugins="modules">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width=120 textAlign="Right" />
<e-column headerText='Customer Name' width=150 :template="'cTemplate'">
<template v-slot:cTemplate={data}>
<div>{{ $t("message.customer") }} - {{data.CustomerName}}</div>
</template>
</e-column>
</e-columns>
</ejs-grid>
</template>
<script setup>
import { i18n } from "./main";
import { GridComponent as EjsGrid, ColumnsDirective as EColumns, ColumnDirective as EColumn } from '@syncfusion/ej2-vue-grids';
const empData = [
{ OrderID: 10248, ShipCountry: "France", CustomerName: "Paul Henriot" },
{ OrderID: 10249, ShipCountry: "Germany", CustomerName: "Karin Josephs" },
{ OrderID: 10250, ShipCountry: "Brazil", CustomerName: "Mario Pontes" },
{ OrderID: 10251, ShipCountry: "France", CustomerName: "Mary Saveley" }
];
const modules= [i18n];
</script>
<style>
@import "../node_modules/@syncfusion/ej2-material3-theme/styles/grid/index.css";
</style>Provide/Inject in templates
In Vue, provide and inject options are used to share data between components that are not directly related through a parent-child relationship.
Syncfusion® components can use these provide and inject options in templates. It allows to pass data from a parent component to its template components without having to pass props down the component tree. Instead, the parent component provides the data, and the child components inject it.
To provide data from a parent component to its template, use the provide option. The provide option is an object that contains the data to provide. The keys in the object are the names of the properties, and the values are the data to provide.
In this below example, the parent component provides the content property with the value of Update in App.vue file.
<template>
<div id="grid">
<ejs-grid ref="grid" :dataSource="ds">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width=120 textAlign="Right" />
<e-column field="CustomerName" headerText="Customer Name" width=150 />
<e-column field="ShipCountry" headerText="Ship Country" width=150 :template="'cTemplate'">
<template v-slot:cTemplate={data}>
<div> <MyTemplate /></div>
</template>
</e-column>
</e-columns>
</ejs-grid>
</div>
</template>
<script setup>
import { provide } from 'vue';
import { GridComponent as EjsGrid, ColumnsDirective as EColumns, ColumnDirective as EColumn } from '@syncfusion/ej2-vue-grids';
import MyTemplate from "./MyTemplate.vue";
var empData = [
{ OrderID: 10248, ShipCountry: "France", CustomerName: "Paul Henriot" },
{ OrderID: 10249, ShipCountry: "Germany", CustomerName: "Karin Josephs" },
{ OrderID: 10250, ShipCountry: "Brazil", CustomerName: "Mario Pontes" },
{ OrderID: 10251, ShipCountry: "France", CustomerName: "Mary Saveley" }
];
provide('content', 'Update');
</script>To inject data provided by a parent component, use the inject option. The inject option is an array or an object that contains the names of the properties to inject.
In this below example, the child template component injects content property using the inject option, and displays its value using an interpolation directive (<h1 id="template-editing-in-vue-grid-component">Template editing in Vue Grid component</h1>
The Vue Data Grid component supports template editing, providing a powerful and flexible way to customize the appearance and behavior of cells during editing. This feature allows you to use Vue templates to define the structure and content of the cells within the grid.
Inline or dialog template editing
The Vue Data Grid provides support for inline and dialog template editing, allowing you to customize the editing using Forms. These forms can be utilized to add and update grid records.
To enable this feature, you need to set the editSettings.mode property of the Grid to either Normal or Dialog and define the grid editors using editSetting.template.
Using Forms
Forms is a approach to create and manipulate the form controls. You can use form to add and update grid records. To use forms for editing operation, you can take leverage of the template support of dialog or inline edit mode. Setting the editSettings.mode as Normal/Dialog and use editSetting.template to define the grid editors.
In some cases, you want to add new field editors in the dialog which are not present in the column model. In that situation the dialog template will help us to customize the default edit dialog.
In the following sample, grid enabled with dialog template editing.
<template>
<div id="app">
<ejs-grid ref="grid" :dataSource='data' :editSettings='editSettings' :actionBegin="actionBegin" :actionComplete="actionComplete" :toolbar='toolbar' height='273px'>
<e-columns>
<e-column field='OrderID' headerText='Order ID' textAlign='Right' :isPrimaryKey='true' width=100></e-column>
<e-column field='CustomerID' headerText='Customer ID' width=120></e-column>
<e-column field='ShipCountry' headerText='Ship Country' width=150></e-column>
</e-columns>
<template v-slot:dialogTemplate="{ data }">
<div formGroup="orderForm">
<div class="form-row">
<div class="form-group col-md-6">
<div class="e-float-input e-control-wrapper">
<input id="OrderID" name="OrderID" v-model='data.OrderID' type="text" :disabled="!data.isAdd" required>
<span class="e-float-line"></span>
<label class="e-float-text e-label-top" for="OrderID"> Order ID</label>
</div>
</div>
<div class="form-group col-md-6">
<div class="e-float-input e-control-wrapper">
<input id="CustomerID" name="CustomerID" v-model='data.CustomerID' type="text" required>
<span class="e-float-line"></span>
<label class="e-float-text e-label-top" for="CustomerID">Customer Name</label>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<ejs-numerictextbox id="Freight" placeholder="Freight" v-model='data.Freight' floatLabelType='Always'></ejs-numerictextbox>
</div>
<div class="form-group col-md-6">
<ejs-datepicker id="OrderDate" placeholder="Order Date" v-model='data.OrderDate' floatLabelType='Always'></ejs-datepicker>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<ejs-dropdownlist id="ShipCountry" v-model='data.ShipCountry' :dataSource='shipCountryDistinctData' :fields="{text: 'ShipCountry', value: 'ShipCountry' }" placeholder="Ship Country" popupHeight='300px' floatLabelType='Always'></ejs-dropdownlist>
</div>
<div class="form-group col-md-6">
<ejs-dropdownlist id="ShipCity" v-model='data.ShipCity' :dataSource='shipCityDistinctData' :fields="{text: 'ShipCity', value: 'ShipCity' }" placeholder="Ship City" popupHeight='300px' floatLabelType='Always'></ejs-dropdownlist>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<ejs-textarea id="ShipAddress" name="ShipAddress" multiline="true" type="text" v-model='data.ShipAddress' floatLabelType="Always" placeholder="ShipAddress"></ejs-textarea>
</div>
</div>
</div>
</template>
</ejs-grid>
</div>
</template>
<script setup>
import { provide } from "vue";
import { GridComponent as EjsGrid, ColumnDirective as EColumn, ColumnsDirective as EColumns, Page, Toolbar, Edit } from "@syncfusion/ej2-vue-grids";
import { data } from './datasource.js';
import { TextAreaComponent as EjsTextarea} from "@syncfusion/ej2-vue-inputs";
import { DatePickerComponent as EjsDatepicker } from "@syncfusion/ej2-vue-calendars";
import { DropDownListComponent as EjsDropdownlist} from "@syncfusion/ej2-vue-dropdowns";
import { NumericTextBoxComponent as EjsNumerictextbox } from "@syncfusion/ej2-vue-inputs";
import { DataUtil } from '@syncfusion/ej2-data';
const editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog', template:'dialogTemplate'}
const toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
provide('grid', [Page, Edit, Toolbar]);
const shipCountryDistinctData= DataUtil.distinct(data, 'ShipCountry', true);
const shipCityDistinctData= DataUtil.distinct(data, 'ShipCity', true);
const actionBegin = (args) => {
if (args.requestType === 'save') {
// cast string to integer value.
args.data['Freight'] = parseFloat(args.form.querySelector("#Freight").value);
}
}
const actionComplete = (args) => {
if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
args.form.ej2_instances[0].addRules('Freight', { max: 500 });
if (args.requestType === 'beginEdit') {
args.form.elements.namedItem('CustomerID').focus();
}
if (args.requestType === 'add') {
args.form.elements.namedItem('OrderID').focus();
}
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material.css";
@import "https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/css/bootstrap.min.css";
.form-group.col-md-6 {
width: 250px;
height: 54px;
}
.form-group.col-md-12 {
height: 72px;
}
.e-input-group.e-multi-line-input.e-auto-width
{
width:100%
}
#ShipAddress {
resize: vertical;
}
</style><template>
<div id="app">
<ejs-grid ref="grid" :dataSource='data' :editSettings='editSettings' :actionBegin="actionBegin" :actionComplete="actionComplete" :toolbar='toolbar' height='273px'>
<e-columns>
<e-column field='OrderID' headerText='Order ID' textAlign='Right' :isPrimaryKey='true' width=100></e-column>
<e-column field='CustomerID' headerText='Customer ID' width=120></e-column>
<e-column field='ShipCountry' headerText='Ship Country' width=150></e-column>
</e-columns>
<template v-slot:dialogTemplate="{ data }">
<div formGroup="orderForm">
<div class="form-row">
<div class="form-group col-md-6">
<div class="e-float-input e-control-wrapper">
<input id="OrderID" name="OrderID" v-model='data.OrderID' type="text" :disabled="!data.isAdd" required>
<span class="e-float-line"></span>
<label class="e-float-text e-label-top" for="OrderID"> Order ID</label>
</div>
</div>
<div class="form-group col-md-6">
<div class="e-float-input e-control-wrapper">
<input id="CustomerID" name="CustomerID" v-model='data.CustomerID' type="text" required>
<span class="e-float-line"></span>
<label class="e-float-text e-label-top" for="CustomerID">Customer Name</label>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<ejs-numerictextbox id="Freight" placeholder="Freight" v-model='data.Freight' floatLabelType='Always'></ejs-numerictextbox>
</div>
<div class="form-group col-md-6">
<ejs-datepicker id="OrderDate" placeholder="Order Date" v-model='data.OrderDate' floatLabelType='Always'></ejs-datepicker>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<ejs-dropdownlist id="ShipCountry" v-model='data.ShipCountry' :dataSource='shipCountryDistinctData' :fields="{text: 'ShipCountry', value: 'ShipCountry' }" placeholder="Ship Country" popupHeight='300px' floatLabelType='Always'></ejs-dropdownlist>
</div>
<div class="form-group col-md-6">
<ejs-dropdownlist id="ShipCity" v-model='data.ShipCity' :dataSource='shipCityDistinctData' :fields="{text: 'ShipCity', value: 'ShipCity' }" placeholder="Ship City" popupHeight='300px' floatLabelType='Always'></ejs-dropdownlist>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<ejs-textarea id="ShipAddress" name="ShipAddress" type="text" v-model='data.ShipAddress' floatLabelType="Always" placeholder="ShipAddress"></ejs-textarea>
</div>
</div>
</div>
</template>
</ejs-grid>
</div>
</template>
<script>
import { GridComponent, ColumnsDirective, ColumnDirective,Toolbar, Edit,Page } from "@syncfusion/ej2-vue-grids";
import { TextAreaComponent } from '@syncfusion/ej2-vue-inputs';
import { data } from './datasource.js';
import { DatePickerComponent } from "@syncfusion/ej2-vue-calendars";
import { DropDownListComponent } from "@syncfusion/ej2-vue-dropdowns";
import { NumericTextBoxComponent } from "@syncfusion/ej2-vue-inputs";
import { DataUtil } from '@syncfusion/ej2-data';
export default {
name: "App",
components: {
"ejs-grid":GridComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective,
"ejs-numerictextbox":NumericTextBoxComponent,
"ejs-datepicker":DatePickerComponent,
"ejs-dropdownlist":DropDownListComponent,
"ejs-textarea":TextAreaComponent
},
data() {
return {
data: data,
editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog', template:'dialogTemplate' },
shipCountryDistinctData: DataUtil.distinct(data, 'ShipCountry', true),
shipCityDistinctData: DataUtil.distinct(data, 'ShipCity', true),
toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel']
};
},
methods: {
actionBegin (args) {
if (args.requestType === 'save') {
args.data['Freight'] = parseFloat(args.form.querySelector("#Freight").value);
}
},
actionComplete(args) {
if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
args.form.ej2_instances[0].addRules('Freight', { max: 500 });
if (args.requestType === 'beginEdit') {
args.form.elements.namedItem('CustomerID').focus();
}
if (args.requestType === 'add') {
args.form.elements.namedItem('OrderID').focus();
}
}
}
},
provide: {
grid: [Page, Edit, Toolbar]
},
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material.css";
@import "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css";
.form-group.col-md-6 {
width: 250px;
height: 54px;
}
.form-group.col-md-12 {
height: 72px;
}
.e-input-group.e-multi-line-input.e-auto-width
{
width:100%
}
#ShipAddress {
resize: vertical;
}
</style>The Dialog/Inline template form editors should have name attribute.
Using template context
You can enhance the customization of your grid’s edit forms by utilizing template contexts, such as accessing row details inside template, rendering editors as components, getting values from editors, setting focus to editors, and disabling default form validation, and adding custom validation. These features are applicable in both inline and dialog editing modes.
The following template context topics are demonstrated through a practical example in the Render tab component inside the dialog template topic.
Access row details inside template using template context
When utilizing edit templates in the Grid , you can access crucial row information within an template when utilizing edit templates. This enables dynamic binding of attributes, values, or elements based on the specific row being edited. This is particularly useful for conditionally rendering or modifying elements in the edit template based on the row’s state.
The following properties will be available at the time of template execution:
| Property Name | Usage |
|---|---|
| isAdd | A Boolean property that defines whether the current row is a new record or not. |
The following code example demonstrates the usage of the isAdd property in an edit template to disable the OrderID textbox when it’s not a new record:
<input id="OrderID" name="OrderID" v-model='data.OrderID' type="text" :disabled="!data.isAdd">Render editors as components
The Vue Data Grid provides a powerful feature that allows you to dynamically render Syncfusion® EJ2 controls as form editors during the editing process. This functionality is particularly useful when you want to provide feature-rich controls for data entry within the edit form.
To achieve this by utilizing the actionComplete event of the Grid and specifying requestType as beginEdit or add.
The following code example illustrates rendering the DropDownList component in the actionComplete event.
const actionComplete = (args) => {
if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
let countryData = DataUtil.distinct(data, 'ShipCountry', true) ;
new DropDownList({value: args.rowData.ShipCountry, popupHeight: '200px', floatLabelType: 'Always',
dataSource: countryData, fields: {text: 'ShipCountry', value: 'ShipCountry'}, placeholder: 'Ship Country'}, args.form.elements.namedItem('ShipCountry'));
}
}Get value from editor
The get value from editor feature in the Vue Data Grid allows you to read, format, and update the current editor value before it is saved. This feature is particularly valuable when you need to perform specific actions on the data, such as formatting or validation, before it is committed to the underlying data source.
To achieve this feature, you can utilize the actionBegin event with the requestType set to save.
In the following code example, the freight value has been formatted and updated.
const actionBegin = (args) => {
if (args.requestType === 'save') {
// cast string to integer value.
args.data.Freight = parseFloat(args.form.querySelector('#Freight').ej2_instances[0] .value);
}
}Set focus to particular column editor
The Vue Data Grid allows you to control the focus behavior of input elements in edit forms. By default, the first input element in the dialog receives focus when the dialog is opened. However, in scenarios where the first input element is disabled or hidden, you can specify which valid input element should receive focus. This can be achieved using the actionComplete event of the Grid, where the requestType is set to beginEdit.
In the following code example, the CustomerID column focused.
const actionComplete = (args) => {
// Set initail Focus
if (args.requestType === 'beginEdit') {
args.form.elements.namedItem('CustomerID').focus();
}
}Disable default form validation
The Vue Data Grid provides built-in support for vue form validation to ensure data integrity and accuracy during editing. However, there might be scenarios where you want to disable the default form validation rules. This can be achieved using the removeRules method within the actionComplete event of the Grid.
To disable default form validation rules in the Grid, follow these steps:
const actionComplete = (args) => {
if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
// Disable the Validation Rules
args.form['ej2_instances'][0].removeRules();
}
}You can use this method to disable validation rules: args.form.ej2_instances[0].rules = {}.
Adding validation rules for custom editors
The Vue Data Grid provides the ability to add validation rules for fields that are not present in the column model. This feature is particularly useful to prevent erroneous or inconsistent data from being submitted, ultimately enhancing the reliability of your application’s data.
To accomplish this, you can utilize the actionComplete event along with the addRules method.
Here’s how you can use the addRules method to add validation rules for custom editors in the actionComplete event:
const actionComplete = (args) => {
if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
// Add Validation Rules
args.form.ej2_instances[0].addRules('Freight', { max: 500 });
}
}Render tab component inside the dialog template
You can enhance the editing experience in the Grid by rendering a Tab component inside the dialog template. This feature is especially useful when you want to present multiple editing sections or categories in a tabbed layout, ensuring a more intuitive and easily navigable interface for data editing.
To enable this functionality, you need to set the editSettings.mode property of the Grid to Dialog. This configures the Grid to use the dialog editing mode. Additionally, you can use the editSetting.template property to define a template variable that contains the Tab component and its corresponding content.
The following example renders a tab component inside the edit dialog. The tab component has two tabs, and once you fill in the first tab and navigate to the second one, the validation for the first tab is performed before navigating to the second.
<template>
<div id="app">
<ejs-grid ref="grid" :dataSource="data" allowPaging="true" :editSettings="editSettings" :toolbar="toolbar" @actionComplete="actionComplete">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width="120" textAlign="Right" isPrimaryKey="true"></e-column>
<e-column field="CustomerID" headerText="Customer Name" width="120" :validationRules='customerIDRules'></e-column>
<e-column field="Freight" headerText="Freight" width="120"></e-column>
<e-column field="ShipCountry" headerText="Ship Country" width="150"></e-column>
<e-column field="ShipAddress" headerText="Ship Address" width="120"></e-column>
<e-column field="Verified" headerText="Verified" width="100" type="boolean" :displayAsCheckBox="true"></e-column>
</e-columns>
<template v-slot:dialogTemplate="{ data }">
<div>
<ejs-tab ref="tabObj" id="tab_wizard" :showCloseButton="false" :selecting='selecting'>
<e-tabitems>
<e-tabitem :header="{ text:'Details'}" :content="'tab1Template'">
</e-tabitem>
<template v-slot:tab1Template>
<div id="tab1">
<div class="form-row">
<div class="form-group col-md-6">
<div class="e-float-input e-control-wrapper" >
<input v-model="data.OrderID" required id="OrderID" name="OrderID" type="text" :disabled="!data.isAdd ? '' : null" :change="onChange"/>
<span class="e-float-line"></span>
<label class="e-float-text e-label-top" for="OrderID">Order ID</label>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<div class="e-float-input e-control-wrapper" >
<input v-model="data.CustomerID" id="CustomerID" required name="CustomerID" type="text" :change="onChange"/>
<span class="e-float-line"></span>
<label class="e-float-text e-label-top" for="CustomerID">Customer Name</label>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<input required id="ShipCountry" name="ShipCountry" type="text" />
</div>
</div>
<button ejs-button type="button" class="e-info e-btn" style="float: right" @click="nextBtn">next
</button>
</div>
</template>
<e-tabitem :header="{ text:'Verify'}" :content="'tab2Template'">
</e-tabitem>
<template v-slot:tab2Template>
<div id="tab2" >
<div class="form-row">
<div class="form-group col-md-6">
<ejs-numerictextbox id="Freight" format="C2" v-model="data.Freight" :change="onChange" placeholder="Freight" floatLabelType="Always" ></ejs-numerictextbox>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<ejs-textarea v-model="data.ShipAddress" id="ShipAddress" name="ShipAddress" floatLabelType="Always" placeholder="ShipAddress" width= 219px
type="text"></ejs-textarea>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<div class="e-float-input e-control-wrapper" >
<ejs-checkbox id="Verified" name="Verified" label="Verified" v-model="data.Verified" ></ejs-checkbox>
</div>
</div>
</div>
<button ejs-button type="button" class="e-info e-btn" style="float: right" @click='submitBtn'>submit</button>
</div>
</template>
</e-tabitems>
</ejs-tab>
</div>
</template>
</ejs-grid>
</div>
</template>
<script setup>
import { provide,ref } from 'vue';
import { GridComponent as EjsGrid, ColumnsDirective as EColumns, ColumnDirective as EColumn, Page, Toolbar, Edit } from '@syncfusion/ej2-vue-grids';
import { DropDownList } from '@syncfusion/ej2-vue-dropdowns';
import { TabComponent as EjsTab, TabItemsDirective as ETabitems, TabItemDirective as ETabitem } from '@syncfusion/ej2-vue-navigations';
import { NumericTextBoxComponent as EjsNumerictextbox } from '@syncfusion/ej2-vue-inputs';
import { CheckBoxComponent as EjsCheckbox } from '@syncfusion/ej2-vue-buttons';
import { TextAreaComponent as EjsTextarea } from '@syncfusion/ej2-vue-inputs';
import { DataUtil } from '@syncfusion/ej2-data';
import { data } from './datasource';
const grid = ref(null);
const tabObj = ref(null);
const formElement = ref(null);
const editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog', template: 'dialogTemplate' };
const toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
const customerIDRules = { required: true };
const actionComplete=function(args) {
if ( args.dialog && args.dialog.element && args.dialog.element.querySelector(".e-footer-content")) {
args.dialog.element.querySelector(".e-footer-content").classList.add("e-hide");
}
if (args.requestType === "beginEdit" || args.requestType === "add") {
formElement.value= args.form;
let countryData = DataUtil.distinct(data, "ShipCountry", true);
new DropDownList(
{
value: args.rowData.ShipCountry,
popupHeight: "200px",
floatLabelType: "Always",
dataSource: countryData,
fields: { text: "ShipCountry", value: "ShipCountry" },
placeholder: "Ship Country",
},
args.form.elements.namedItem("ShipCountry")
);
args.form.ej2_instances[0].removeRules();
args.form.ej2_instances[0].addRules("Freight", { min: 1, max: 500 });
if (args.requestType === "beginEdit") {
setTimeout(() => {
args.form.elements.namedItem("CustomerID").focus();
}, 200);
}
if (args.requestType === "add") {
args.form.elements.namedItem("OrderID").focus();
}
}
};
const nextBtn = () => {
moveNext();
};
const moveNext = () => {
if (validate(1)) {
tabObj.value.select(1);
}
};
const selecting = (e) => {
if (e.isSwiped) {
e.cancel = true;
}
};
const submitBtn = () => {
if (validate(2)) {
grid.value.ej2Instances.endEdit();
}
};
const onChange = () => {
formElement.value.ej2_instances[0].validate();
};
const validate = (tab) => {
let valid= true;
[].slice.call(document.getElementById('tab' + tab).querySelectorAll('[name]')).forEach(element => {
setTimeout(() => {
if ( element.form && element.form.ej2_instances && element.form.ej2_instances.length > 0) {
element.form.ej2_instances[0].validate(element.name);
}
}, 100);
if (element.getAttribute('aria-invalid') === 'true'){
valid = false;
}
});
if (!valid) {
return false;
}
return true;
};
provide('grid', [Page, Edit, Toolbar]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material.css";
@import "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css";
.form-group.col-md-6 {
width: 250px;
height: 54px;
}
.form-group.col-md-12 {
height: 72px;
}
#ShipAddress {
resize: vertical;
}
</style><template>
<div id="app">
<ejs-grid ref="grid" :dataSource="data" allowPaging="true" :editSettings="editSettings" :toolbar="toolbar" @actionComplete="actionComplete">
<e-columns>
<e-column field="OrderID" headerText="Order ID" width="120" textAlign="Right" isPrimaryKey="true"></e-column>
<e-column field="CustomerID" headerText="Customer Name" width="120" :validationRules='customerIDRules'></e-column>
<e-column field="Freight" headerText="Freight" width="120"></e-column>
<e-column field="ShipCity" headerText="Ship City" width="120"></e-column>
<e-column field="ShipCountry" headerText="Ship Country" width="150"></e-column>
<e-column field="ShipAddress" headerText="Ship Address" width="120"></e-column>
<e-column field="Verified" headerText="Verified" width="100" type="boolean" :displayAsCheckBox="true"></e-column>
</e-columns>
<template v-slot:dialogTemplate="{ data }">
<div>
<ejs-tab ref="tabObj" id="tab_wizard" :showCloseButton="false" :selecting='selecting'>
<e-tabitems>
<e-tabitem :header="{ text:'Details'}" :content="'tab1Template'">
</e-tabitem>
<template v-slot:tab1Template>
<div id="tab1">
<div class="form-row">
<div class="form-group col-md-6">
<div class="e-float-input e-control-wrapper" >
<input v-model="data.OrderID" required id="OrderID" name="OrderID" type="text" :disabled="!data.isAdd ? '' : null" :change="onChange"/>
<span class="e-float-line"></span>
<label class="e-float-text e-label-top" for="OrderID">Order ID</label>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<div class="e-float-input e-control-wrapper" >
<input v-model="data.CustomerID" id="CustomerID" required name="CustomerID" type="text" :change="onChange"/>
<span class="e-float-line"></span>
<label class="e-float-text e-label-top" for="CustomerID">Customer Name</label>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<input required id="ShipCountry" name="ShipCountry" type="text" />
</div>
</div>
<button ejs-button type="button" class="e-info e-btn" style="float: right" @click="nextBtn">next
</button>
</div>
</template>
<e-tabitem :header="{ text:'Verify'}" :content="'tab2Template'">
</e-tabitem>
<template v-slot:tab2Template>
<div id="tab2" >
<div class="form-row">
<div class="form-group col-md-6">
<ejs-numerictextbox id="Freight" format="C2" v-model="data.Freight" :change="onChange" placeholder="Freight" floatLabelType="Always" ></ejs-numerictextbox>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<ejs-textarea v-model="data.ShipAddress" id="ShipAddress" name="ShipAddress" floatLabelType="Always" placeholder="ShipAddress" width= 219px
type="text"></ejs-textarea>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<ejs-checkbox id="Verified" name="Verified" label="Verified" v-model="data.Verified" ></ejs-checkbox>
</div>
</div>
<button ejs-button type="button" class="e-info e-btn" style="float: right" @click='submitBtn'>submit</button>
</div>
</template>
</e-tabitems>
</ejs-tab>
</div>
</template>
</ejs-grid>
</div>
</template>
<script>
import { GridComponent, ColumnsDirective, ColumnDirective, Page, Toolbar, Edit } from "@syncfusion/ej2-vue-grids";
import { TabComponent,TabItemDirective,TabItemsDirective } from "@syncfusion/ej2-vue-navigations";
import { NumericTextBoxComponent } from "@syncfusion/ej2-vue-inputs";
import { DropDownList } from '@syncfusion/ej2-dropdowns';
import { TextAreaComponent } from '@syncfusion/ej2-vue-inputs';
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";
import { data } from './datasource.js';
import { DataUtil } from '@syncfusion/ej2-data';
export default {
name: "App",
components: {
"ejs-grid": GridComponent,
"e-columns": ColumnsDirective,
"e-column": ColumnDirective,
"ejs-numerictextbox": NumericTextBoxComponent,
"ejs-checkbox": CheckBoxComponent,
"ejs-tab": TabComponent,
"e-tabitem": TabItemDirective,
"e-tabitems": TabItemsDirective,
"ejs-textarea":TextAreaComponent
},
data() {
return {
data: data,
formElement:"",
editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog', template: "dialogTemplate" },
shipCountryDistinctData: DataUtil.distinct(data, 'ShipCountry', true),
shipCityDistinctData: DataUtil.distinct(data, 'ShipCity', true),
toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel'],
currentTab: 0,
customerIDRules: { required: true }
};
},
methods: {
actionComplete(args) {
if ( args.dialog && args.dialog.element && args.dialog.element.querySelector(".e-footer-content")) {
args.dialog.element.querySelector(".e-footer-content").classList.add("e-hide");
}
if (args.requestType === "beginEdit" || args.requestType === "add") {
this.formElement = args.form;
let countryData = DataUtil.distinct(data, "ShipCountry", true);
new DropDownList(
{
value: args.rowData.ShipCountry,
popupHeight: "200px",
floatLabelType: "Always",
dataSource: countryData,
fields: { text: "ShipCountry", value: "ShipCountry" },
placeholder: "Ship Country",
},
args.form.elements.namedItem("ShipCountry")
);
args.form.ej2_instances[0].removeRules();
args.form.ej2_instances[0].addRules("Freight", { min: 1, max: 500 });
if (args.requestType === "beginEdit") {
args.form.elements.namedItem("CustomerID").focus();
}
if (args.requestType === "add") {
args.form.elements.namedItem("OrderID").focus();
}
}
},
nextBtn() {
this.moveNext();
},
moveNext() {
if (this.validate(1)) {
this.$refs.tabObj.select(1);
}
},
selecting(e) {
if(e.isSwiped ){
e.cancel = true;
}
},
submitBtn() {
if (this.validate(2)) {
this.$refs.grid.ej2Instances.endEdit();
}
},
onChange() {
this.formElement["ej2_instances"][0].validate();
},
validate(tab) {
let valid= true;
[].slice.call(document.getElementById('tab' + tab).querySelectorAll('[name]')).forEach(element => {
setTimeout(() => {
if ( element.form && element.form.ej2_instances && element.form.ej2_instances.length > 0) {
element.form.ej2_instances[0].validate(element.name);
}
}, 100);
if (element.getAttribute('aria-invalid') === 'true'){
valid = false;
}
});
if (!valid) {
return false;
}
return true;
},
},
provide: {
grid: [Page, Edit, Toolbar],
},
};
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css";
.form-group.col-md-6 {
width: 250px;
float: none;
}
#ShipAddress {
resize: vertical;
}
</style>) in MyTemplate.vue file.
<template>
<ejs-button>{{ content }}</ejs-button>
</template>
<script>
import { inject } from 'vue';
import { ButtonComponent as EjsButton } from '@syncfusion/ej2-vue-buttons';
const content = inject('content');
</script>