Templates in Vue Query builder component
11 Jun 202424 minutes to read
Templates allows users to define customized header and own user interface for columns.
Header Template
Header Template allows to define your own user interface for Header, which includes creating or deleting rules and groups and to customize the AND/OR condition and NOT condition options. To implement header template, you can create the user interface as vue
component and assign the values when requestType is header-template-create in actionBegin
event.
In the following sample dropdown, splitbutton and button are used as the custom components in the header.
<template>
<div class="control-section">
<ejs-querybuilder id="querybuilder" ref="querybuilder" :dataSource="dataSource" :rule="importRules" :headerTemplate="headerTemplate" enableNotCondition = true>
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"/>
<e-column field="FirstName" label="First Name" type="string"/>
<e-column field="Age" label="Age" type="number" />
<e-column field='City' label='City' type='string' />
<e-column field='Country' label='Country' type='string' />
</e-columns>
</ejs-querybuilder>
</div>
</template>
<script setup>
import { QueryBuilderComponent as EjsQuerybuilder, ColumnsDirective as EColumns, ColumnDirective as EColumn } from '@syncfusion/ej2-vue-querybuilder';
import { DropDownListComponent } from "@syncfusion/ej2-vue-dropdowns";
import { getComponent, closest } from '@syncfusion/ej2-base';
import { CheckBoxComponent, ButtonComponent } from "@syncfusion/ej2-vue-buttons";
import { DropDownButtonComponent } from "@syncfusion/ej2-vue-splitbuttons";
import {createApp, ref} from 'vue';
const app = createApp({});
const querybuilder = ref(null);
const dataSource = employeeData;
const importRules = {
'condition': 'and', not: true,
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'equal',
'value': 34
},
{
'label': 'FirstName',
'field': 'FirstName',
'type': 'string',
'operator': 'equal',
'value': 'Nancy'
},
{
'condition': 'or',
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'equal',
'value': 34
}]
}]
};
const headTemplate = app.component('headerTemplate', {
components: {
'ejs-checkbox': CheckBoxComponent,
'ejs-dropdownlist': DropDownListComponent,
'ejs-dropdownbutton': DropDownButtonComponent,
'ejs-button': ButtonComponent
},
template:
`<div class="e-groupheader">
<button v-if="data.notCondition !== undefined" class='e-cb-wrapper'>
<ejs-checkbox :id="notID" label='not' :checked="data.notCondition" :change="onChange"></ejs-checkbox>
</button>
<ejs-dropdownlist :id="ddlID" cssClass="e-custom-group-btn" :dataSource="ds" :fields="fields" v-model="data.condition" :change="conditionChange"></ejs-dropdownlist>
<ejs-dropdownbutton :id = 'ddbID' :items='ddbitems' cssClass= "e-round e-small e-caret-hide e-addrulegroup e-add-btn" iconCss="e-icons e-add-icon" :select='onSelect'></ejs-dropdownbutton>
<ejs-button v-if="data.ruleID !== 'querybuilder_group0'" :id="dltbtnID" class= "e-btn e-delete-btn e-lib e-small e-round e-icon-btn" iconCss="e-btn-icon e-icons e-delete-icon" v-on:click='btnClick'>
</ejs-button>
</div>`,
data() {
return {
qryBldrObj: getComponent(document.getElementById('querybuilder'), 'query-builder'),
ds: [{'key': 'AND', 'value': 'and'},{'key': 'OR', 'value': 'or'}],
fields: { text: 'key', value: 'value' },
ddbitems:[
{
text: 'AddGroup',
iconCss: 'e-icons e-add-icon e-addgroup'
},
{
text: 'AddCondition',
iconCss: 'e-icons e-add-icon e-addrule'
}
]
}
},
computed: {
ddbID: function(){
return `${this.data.ruleID}_addbtn`;
},
notID: function(){
return `${this.data.ruleID}_notOption`;
},
ddlID: function(){
return `${this.data.ruleID}_cndtn`;
},
dltbtnID: function(){
return `${this.data.ruleID}_dltbtn`;
}
},
methods: {
onSelect: function(event){
var addbtn = closest(event.element,'.e-dropdown-popup');
var ddbId = addbtn.id; var ddb = ddbId.split('_');
if (event.item.text === 'AddGroup') {
this.qryBldrObj.addGroups([{condition: 'or', 'rules': [{}], not: false}], ddb[1]);
} else if (event.item.text === 'AddCondition') {
this.qryBldrObj.addRules([{}], ddb[1]);
}
},
onChange: function(args){
this.qryBldrObj.notifyChange(args.checked, args.event.target, 'not');
},
conditionChange: function(args){
this.qryBldrObj.notifyChange(args.value, args.element, 'condition');
},
btnClick: function(args){
this.qryBldrObj.deleteGroup(closest(args.target.offsetParent, '.e-group-container'));
}
}
});
const headerTemplate = function() {
return {
template: headTemplate
}
};
var employeeData = [
{ 'EmployeeID': 1, 'FirstName': 'Nancy', 'Age': 31, 'City': 'Seattle', 'Country': 'USA' },
{ 'EmployeeID': 2, 'FirstName': 'Andrew', 'Age': 32, 'City': 'Tacoma', 'Country': 'USA' },
{ 'EmployeeID': 3, 'FirstName': 'Janet', 'Age': 33, 'City': 'Kirkland', 'Country': 'USA' },
{ 'EmployeeID': 4, 'FirstName': 'Margaret', 'Age': 33, 'City': 'Redmond', 'Country': 'USA' },
{ 'EmployeeID': 5, 'FirstName': 'Steven', 'Age': 34, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 6, 'FirstName': 'Michael', 'Age': 35, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 7, 'FirstName': 'Robert', 'Age': 36, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 8, 'FirstName': 'Laura', 'Age': 37, 'City': 'Seattle', 'Country': 'USA' },
{ 'EmployeeID': 9, 'FirstName': 'Anne', 'Age': 38, 'City': 'London', 'Country': 'UK' }
]
</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-splitbuttons/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-lists/styles/material.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material.css";
@import "../node_modules/@syncfusion/ej2-vue-querybuilder/styles/material.css";
.e-query-builder {
margin: 0 auto;
}
.e-rule-template {
padding-bottom: 12px;
}
.e-query-builder .e-add-btn {
margin-left: 10px;
margin-right: 10px;
}
.e-query-builder .cndtnbtn.e-control.e-dropdownlist.e-lib.e-input {
padding-left: 10px;
}
.e-query-builder span.e-custom-group-btn {
max-width: 100px;
border-radius: 5px !important;
border-width: 1px !important;
}
.e-query-builder .e-custom-group-btn.e-input-focus::before, .e-custom-group-btn.e-input-focus::after {
background: transparent !important;
}
.e-query-builder .e-group-header .e-addrulegroup, .e-group-header .e-delete-btn {
border: 1px solid grey !important;
}
.e-query-builder .e-group-header .e-addrulegroup:hover, .e-group-header .e-delete-btn:hover {
border: 1px solid grey !important;
}
.e-query-builder .e-toggle{
background: #317ab9;
border-color: #317ab9;
color: #fff;
}
.e-query-builder .e-cb-wrapper {
margin-right: 5px;
height: 32px;
border-radius: 5px;
border: 1px solid gray;
background-color: white;
}
.e-query-builder .e-custom-group-btn{
padding-left: 10px;
height: 32px;
}
.e-control {
display: inline;
}
.e-query-builder div.e-rule-container{
width: 800px;
}
</style>
<template>
<div class="control-section">
<ejs-querybuilder id="querybuilder" ref="querybuilder" :dataSource="dataSource" :rule="importRules" :headerTemplate="headerTemplate" enableNotCondition = true>
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"/>
<e-column field="FirstName" label="First Name" type="string"/>
<e-column field="Age" label="Age" type="number" />
<e-column field='City' label='City' type='string' />
<e-column field='Country' label='Country' type='string' />
</e-columns>
</ejs-querybuilder>
</div>
</template>
<script>
import { QueryBuilderComponent, ColumnDirective, ColumnsDirective } from '@syncfusion/ej2-vue-querybuilder';
import { DropDownListComponent } from "@syncfusion/ej2-vue-dropdowns";
import { getComponent, closest } from '@syncfusion/ej2-base';
import { CheckBoxComponent, ButtonComponent } from "@syncfusion/ej2-vue-buttons";
import { DropDownButtonComponent } from "@syncfusion/ej2-vue-splitbuttons";
import {createApp} from 'vue';
const app = createApp({});
const headTemplate = app.component('headerTemplate', {
components: {
'ejs-checkbox': CheckBoxComponent,
'ejs-dropdownlist': DropDownListComponent,
'ejs-dropdownbutton': DropDownButtonComponent,
'ejs-button': ButtonComponent
},
template:
`<div class="e-groupheader">
<button v-if="data.notCondition !== undefined" class='e-cb-wrapper'>
<ejs-checkbox :id="notID" label='not' :checked="data.notCondition" :change="onChange"></ejs-checkbox>
</button>
<ejs-dropdownlist :id="ddlID" cssClass="e-custom-group-btn" :dataSource="ds" :fields="fields" v-model="data.condition" :change="conditionChange"></ejs-dropdownlist>
<ejs-dropdownbutton :id = 'ddbID' :items='ddbitems' cssClass= "e-round e-small e-caret-hide e-addrulegroup e-add-btn" iconCss="e-icons e-add-icon" :select='onSelect'></ejs-dropdownbutton>
<ejs-button v-if="data.ruleID !== 'querybuilder_group0'" :id="dltbtnID" class= "e-btn e-delete-btn e-lib e-small e-round e-icon-btn" iconCss="e-btn-icon e-icons e-delete-icon" v-on:click='btnClick'>
</ejs-button>
</div>`,
data() {
return {
qryBldrObj: getComponent(document.getElementById('querybuilder'), 'query-builder'),
ds: [{'key': 'AND', 'value': 'and'},{'key': 'OR', 'value': 'or'}],
fields: { text: 'key', value: 'value' },
ddbitems:[
{
text: 'AddGroup',
iconCss: 'e-icons e-add-icon e-addgroup'
},
{
text: 'AddCondition',
iconCss: 'e-icons e-add-icon e-addrule'
}
]
}
},
computed: {
ddbID: function(){
return `${this.data.ruleID}_addbtn`;
},
notID: function(){
return `${this.data.ruleID}_notOption`;
},
ddlID: function(){
return `${this.data.ruleID}_cndtn`;
},
dltbtnID: function(){
return `${this.data.ruleID}_dltbtn`;
}
},
methods: {
onSelect: function(event){
var addbtn = closest(event.element,'.e-dropdown-popup');
var ddbId = addbtn.id; var ddb = ddbId.split('_');
if (event.item.text === 'AddGroup') {
this.qryBldrObj.addGroups([{condition: 'or', 'rules': [{}], not: false}], ddb[1]);
} else if (event.item.text === 'AddCondition') {
this.qryBldrObj.addRules([{}], ddb[1]);
}
},
onChange: function(args){
this.qryBldrObj.notifyChange(args.checked, args.event.target, 'not');
},
conditionChange: function(args){
this.qryBldrObj.notifyChange(args.value, args.element, 'condition');
},
btnClick: function(args){
this.qryBldrObj.deleteGroup(closest(args.target.offsetParent, '.e-group-container'));
}
}
});
export default {
name: "App",
components: {
"ejs-querybuilder":QueryBuilderComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective,
},
data: function() {
return {
dataSource: employeeData,
importRules: {
'condition': 'and', 'not': true,
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'equal',
'value': 34
},
{
'label': 'FirstName',
'field': 'FirstName',
'type': 'string',
'operator': 'equal',
'value': 'Nancy'
},
{
'condition': 'or',
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'equal',
'value': 34
}]
}]
},
headerTemplate: () => {
return {
template : headTemplate
}
}
};
}
}
var employeeData = [
{ 'EmployeeID': 1, 'FirstName': 'Nancy', 'Age': 31, 'City': 'Seattle', 'Country': 'USA' },
{ 'EmployeeID': 2, 'FirstName': 'Andrew', 'Age': 32, 'City': 'Tacoma', 'Country': 'USA' },
{ 'EmployeeID': 3, 'FirstName': 'Janet', 'Age': 33, 'City': 'Kirkland', 'Country': 'USA' },
{ 'EmployeeID': 4, 'FirstName': 'Margaret', 'Age': 33, 'City': 'Redmond', 'Country': 'USA' },
{ 'EmployeeID': 5, 'FirstName': 'Steven', 'Age': 34, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 6, 'FirstName': 'Michael', 'Age': 35, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 7, 'FirstName': 'Robert', 'Age': 36, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 8, 'FirstName': 'Laura', 'Age': 37, 'City': 'Seattle', 'Country': 'USA' },
{ 'EmployeeID': 9, 'FirstName': 'Anne', 'Age': 38, 'City': 'London', 'Country': 'UK' }
]
</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-splitbuttons/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-lists/styles/material.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material.css";
@import "../node_modules/@syncfusion/ej2-vue-querybuilder/styles/material.css";
.e-query-builder {
margin: 0 auto;
}
.e-rule-template {
padding-bottom: 12px;
}
.e-query-builder .e-add-btn {
margin-left: 10px;
margin-right: 10px;
}
.e-query-builder .cndtnbtn.e-control.e-dropdownlist.e-lib.e-input {
padding-left: 10px;
}
.e-query-builder span.e-custom-group-btn {
max-width: 100px;
border-radius: 5px !important;
border-width: 1px !important;
}
.e-query-builder .e-custom-group-btn.e-input-focus::before, .e-custom-group-btn.e-input-focus::after {
background: transparent !important;
}
.e-query-builder .e-group-header .e-addrulegroup, .e-group-header .e-delete-btn {
border: 1px solid grey !important;
}
.e-query-builder .e-group-header .e-addrulegroup:hover, .e-group-header .e-delete-btn:hover {
border: 1px solid grey !important;
}
.e-query-builder .e-toggle{
background: #317ab9;
border-color: #317ab9;
color: #fff;
}
.e-query-builder .e-cb-wrapper {
margin-right: 5px;
height: 32px;
border-radius: 5px;
border: 1px solid gray;
background-color: white;
}
.e-query-builder .e-custom-group-btn{
padding-left: 10px;
height: 32px;
}
.e-control {
display: inline;
}
.e-query-builder div.e-rule-container{
width: 800px;
}
</style>
Column Template
Template allows you to define your own input widgets for columns. To implement template
, you can define the following functions
-
create
: Creates the custom component. -
write
: Wire events for the custom component. -
Destroy
: Destroy the custom component.
In the following sample, dropdown is used as the custom component in the PaymentMode column.
<template>
<div class="control-section">
<div class="col-lg-12 querybuilder-control">
<ejs-querybuilder ref="querybuilder" :dataSource="dataSource" :rule="importRules" width="70%">
<e-columns>
<e-column field='Category' label='Category' type='string' />
<e-column field='PaymentMode' label='Payment Mode' type='string' :template='paymentTemplate' />
<e-column field='TransactionType' label='Transaction Type' type='boolean' />
<e-column field='Description' label='Description' type='string' />
<e-column field='Date' label='Date' type='date' />
<e-column field='Amount' label='Amount' type='number' />
</e-columns>
</ejs-querybuilder>
</div>
</div>
</template>
<script setup>
import { QueryBuilderComponent as EjsQuerybuilder, ColumnDirective as EColumn, ColumnsDirective as EColumns } from "@syncfusion/ej2-vue-querybuilder";
import { DropDownList } from '@syncfusion/ej2-dropdowns'
import { createElement, getComponent} from "@syncfusion/ej2-base";
import { ref } from "vue";
const querybuilder = ref(null);
let inOperators = ['in', 'notin'];
const dataSource = expenseData;
const paymentTemplate = {
create: () => {
return createElement('input', { attrs: { 'type': 'text' } });
},
destroy: (args) => {
let multiselect = getComponent(document.getElementById(args.elementId), 'multiselect');
if(multiselect)
multiselect.destroy();
let dropdownlist = getComponent(document.getElementById(args.elementId), 'dropdownlist');
if(dropdownlist)
dropdownlist.destroy();
},
write: (args) => {
let ds = ['Cash', 'Debit Card', 'Credit Card', 'Net Banking', 'Wallet'];
if (inOperators.indexOf(args.operator) > -1) {
let multiSelectObj = new MultiSelect({
dataSource: ds,
value: args.values,
mode: 'CheckBox',
placeholder: 'Select Transaction',
change: (e) => {
querybuilder.value.ej2Instances[0].notifyChange(e.value, e.element);
}
});
multiSelectObj.appendTo('#' + args.elements.id);
} else {
let dropDownObj = new DropDownList({
dataSource: ds,
value: args.values,
change: (e) => {
querybuilder.value.ej2Instances[0].notifyChange(e.itemData.value, e.element);
}
});
dropDownObj.appendTo('#' + args.elements.id);
}
}
};
const importRules = {
'condition': 'and',
'rules': [{
'label': 'Payment Mode',
'field': 'PaymentMode',
'type': 'string',
'operator': 'equal',
'value': 'Cash'
}]
};
var expenseData = [{
'Category': 'Food',
'PaymentMode': 'Credit Card',
'TransactionType': 'Expense',
'Description': 'Boiled peanuts',
'Amount': '7',
'Date': '06/01/2017'
}, {
'Category': 'Food',
'PaymentMode': 'Cash',
'TransactionType': 'Expense',
'Description': 'Peanuts in Coke',
'Amount': '8',
'Date': '06/04/2017'
}, {
'Category': 'Food',
'PaymentMode': 'Cash',
'TransactionType': 'Expense',
'Description': 'Palmetto Cheese, Mint julep',
'Amount': '11',
'Date': '06/04/2017'
}];
</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-splitbuttons/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-lists/styles/material.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material.css";
@import "../node_modules/@syncfusion/ej2-vue-querybuilder/styles/material.css";
.e-query-builder {
margin: 0 auto;
}
</style>
<template>
<div class="control-section">
<div class="col-lg-12 querybuilder-control">
<ejs-querybuilder ref="querybuilder" :dataSource="dataSource" :rule="importRules" width="70%">
<e-columns>
<e-column field='Category' label='Category' type='string' />
<e-column field='PaymentMode' label='Payment Mode' type='string' :template='paymentTemplate' />
<e-column field='TransactionType' label='Transaction Type' type='boolean' />
<e-column field='Description' label='Description' type='string' />
<e-column field='Date' label='Date' type='date' />
<e-column field='Amount' label='Amount' type='number' />
</e-columns>
</ejs-querybuilder>
</div>
</div>
</template>
<script>
import { QueryBuilderComponent, ColumnDirective, ColumnsDirective } from "@syncfusion/ej2-vue-querybuilder";
import { DropDownList, MultiSelect } from '@syncfusion/ej2-dropdowns'
import { createElement, getComponent} from "@syncfusion/ej2-base";
let inOperators = ['in', 'notin'];
export default {
name: "App",
components: {
"ejs-querybuilder":QueryBuilderComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective,
},
data: function() {
return {
dataSource: expenseData,
paymentTemplate: {
create: () => {
return createElement('input', { attrs: { 'type': 'text' } });
},
destroy: (args) => {
let multiselect = getComponent(document.getElementById(args.elementId), 'multiselect');
if(multiselect)
multiselect.destroy();
let dropdownlist = getComponent(document.getElementById(args.elementId), 'dropdownlist');
if(dropdownlist)
dropdownlist.destroy();
},
write: (args) => {
let ds = ['Cash', 'Debit Card', 'Credit Card', 'Net Banking', 'Wallet'];
if (inOperators.indexOf(args.operator) > -1) {
let multiSelectObj = new MultiSelect({
dataSource: ds,
value: args.values,
mode: 'CheckBox',
placeholder: 'Select Transaction',
change: (e) => {
this.$refs.querybuilder.$el.ej2_instances[0].notifyChange(e.value, e.element);
}
});
multiSelectObj.appendTo('#' + args.elements.id);
} else {
let dropDownObj = new DropDownList({
dataSource: ds,
value: args.values,
change: (e) => {
this.$refs.querybuilder.$el.ej2_instances[0].notifyChange(e.itemData.value, e.element);
}
});
dropDownObj.appendTo('#' + args.elements.id);
}
}
},
importRules: {
'condition': 'and',
'rules': [{
'label': 'Payment Mode',
'field': 'PaymentMode',
'type': 'string',
'operator': 'equal',
'value': 'Cash'
}]
}
};
}
}
var expenseData = [{
'Category': 'Food',
'PaymentMode': 'Credit Card',
'TransactionType': 'Expense',
'Description': 'Boiled peanuts',
'Amount': '7',
'Date': '06/01/2017'
}, {
'Category': 'Food',
'PaymentMode': 'Cash',
'TransactionType': 'Expense',
'Description': 'Peanuts in Coke',
'Amount': '8',
'Date': '06/04/2017'
}, {
'Category': 'Food',
'PaymentMode': 'Cash',
'TransactionType': 'Expense',
'Description': 'Palmetto Cheese, Mint julep',
'Amount': '11',
'Date': '06/04/2017'
}];
</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-splitbuttons/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-lists/styles/material.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material.css";
@import "../node_modules/@syncfusion/ej2-vue-querybuilder/styles/material.css";
.e-query-builder {
margin: 0 auto;
}
</style>
Using Template
Template allows you to define your own input widgets for columns. To implement template, you can create the user interface as vue
component.
<template>
<div class="control-section">
<ejs-querybuilder id="querybuilder" ref="querybuilder" :rule="importRules" width="100%" >
<e-columns>
<e-column field='Category' label='Category' type='string'/>
<e-column field='PaymentMode' label='Payment Mode' type='string' :operators="customOperators" :template='paymentTemplate' />
<e-column field='TransactionType' label='Transaction Type' type='string' :operators="customOperators" :template='transactionTemplate' />
<e-column field='Description' label='Description' type='string' />
<e-column field='Date' label='Date' type='date' />
<e-column field='Amount' label='Amount' type='number' />
</e-columns>
</ejs-querybuilder>
</div>
</template>
<script setup>
import { QueryBuilderComponent as EjsQuerybuilder, ColumnDirective as EColumn, ColumnsDirective as EColumns } from "@syncfusion/ej2-vue-querybuilder";
import { MultiSelect, CheckBoxSelection } from '@syncfusion/ej2-dropdowns';
import { getComponent } from "@syncfusion/ej2-base";
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";
import { DropDownListComponent } from "@syncfusion/ej2-vue-dropdowns";
import {createApp} from "vue";
MultiSelect.Inject(CheckBoxSelection);
const app = createApp({});
const payTemplate = app.component('paymentTemplate', {
components: {
"ejs-dropdownlist": DropDownListComponent
},
template:
`<div >
<ejs-dropdownlist :dataSource="ds" v-model="data.rule.value" :change="paymentChange"></ejs-dropdownlist>
</div>`,
data(args) {
return{
qryBldrObj: getComponent(document.getElementById('querybuilder'), 'query-builder'),
data: Object.assign({}, args, true),
ds: ['Cash', 'Debit Card', 'Credit Card', 'Net Banking']
}
},
methods: {
paymentChange: function(event){
var elem = document.getElementById(ruleID).querySelector('.e-rule-value');
this.qryBldrObj.notifyChange(event.value, elem, 'value');
}
}
});
const transTemplate = app.component('transactionTemplate', {
components: {
"ejs-checkbox": CheckBoxComponent
},
template:
`<div >
<ejs-checkbox label='Is Expense' :checked="data.rule.value" :change="transactionChange"></ejs-checkbox>
</div>`,
data(args) {
return{
qryBldrObj: getComponent(document.getElementById('querybuilder'), 'query-builder'),
data: Object.assign({}, args, true)
}
},
methods: {
transactionChange: function(event){
var elem = document.getElementById(ruleID).querySelector('.e-rule-value');
this.qryBldrObj.notifyChange(event.checked === true ? 'Expense' : 'Income', elem, 'value');
}
}
});
const customOperators = [{value: 'equal', key: 'Equal'}, {value: 'notequal', key: 'Not Equal'}];
const importRules = {
'condition': 'or',
'rules': [{
'label': 'TransactionType',
'field': 'TransactionType',
'type': 'boolean',
'operator': 'equal',
'value': 'Income'
},
{
'label': 'PaymentMode',
'field': 'PaymentMode',
'type': 'string',
'operator': 'equal',
'value': 'Cash'
}]
};
const paymentTemplate = () => {
return {
template: payTemplate
}
};
const transactionTemplate = () => {
return {
template: transTemplate
}
};
</script>
<template>
<div class="control-section">
<ejs-querybuilder id="querybuilder" ref="querybuilder" :rule="importRules" width="100%" >
<e-columns>
<e-column field='Category' label='Category' type='string'/>
<e-column field='PaymentMode' label='Payment Mode' type='string' :operators="customOperators" :template='paymentTemplate' />
<e-column field='TransactionType' label='Transaction Type' type='string' :operators="customOperators" :template='transactionTemplate' />
<e-column field='Description' label='Description' type='string' />
<e-column field='Date' label='Date' type='date' />
<e-column field='Amount' label='Amount' type='number' />
</e-columns>
</ejs-querybuilder>
</div>
</template>
<script>
import { QueryBuilderComponent } from "@syncfusion/ej2-vue-querybuilder";
import { MultiSelect, CheckBoxSelection } from '@syncfusion/ej2-dropdowns';
import { getComponent } from "@syncfusion/ej2-base";
import { CheckBoxComponent } from "@syncfusion/ej2-vue-buttons";
import { DropDownListComponent } from "@syncfusion/ej2-vue-dropdowns";
import {createApp} from "vue";
MultiSelect.Inject(CheckBoxSelection);
const app = createApp({});
const payTemplate = app.component('paymentTemplate', {
components: {
"ejs-dropdownlist": DropDownListComponent
},
template:
`<div >
<ejs-dropdownlist :dataSource="ds" v-model="data.rule.value" :change="paymentChange"></ejs-dropdownlist>
</div>`,
data(args) {
return{
qryBldrObj: getComponent(document.getElementById('querybuilder'), 'query-builder'),
data: Object.assign({}, args, true),
ds: ['Cash', 'Debit Card', 'Credit Card', 'Net Banking']
}
},
methods: {
paymentChange: function(event){
var elem = document.getElementById(ruleID).querySelector('.e-rule-value');
this.qryBldrObj.notifyChange(event.value, elem, 'value');
}
}
});
const transTemplate = app.component('transactionTemplate', {
components: {
"ejs-checkbox": CheckBoxComponent
},
template:
`<div >
<ejs-checkbox label='Is Expense' :checked="data.rule.value" :change="transactionChange"></ejs-checkbox>
</div>`,
data(args) {
return{
qryBldrObj: getComponent(document.getElementById('querybuilder'), 'query-builder'),
data: Object.assign({}, args, true)
}
},
methods: {
transactionChange: function(event){
var elem = document.getElementById(ruleID).querySelector('.e-rule-value');
this.qryBldrObj.notifyChange(event.checked === true ? 'Expense' : 'Income', elem, 'value');
}
}
});
export default {
name: "App",
components: {
"ejs-querybuilder":QueryBuilderComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective,
},
data: function() {
return {
customOperators: [{value: 'equal', key: 'Equal'}, {value: 'notequal', key: 'Not Equal'}],
importRules:{
'condition': 'or',
'rules': [{
'label': 'TransactionType',
'field': 'TransactionType',
'type': 'boolean',
'operator': 'equal',
'value': 'Income'
},
{
'label': 'PaymentMode',
'field': 'PaymentMode',
'type': 'string',
'operator': 'equal',
'value': 'Cash'
}]
},
paymentTemplate: () => {
return {
template: payTemplate
}
},
transactionTemplate: () => {
return{
template: transTemplate
}
}
};
}
}
</script>
Rule Template
Rule Template allows to define your own user interface for columns. To implement ruleTemplate
, you can create the user interface as vue
component and assign the values through actionBegin
event.
In the following sample, dropdown and slider are used as the custom component and applied greaterthanorequal
operator to Age
column.
<template>
<div class="control-section">
<div id="listbox-selection">
<ejs-querybuilder id="querybuilder" ref="querybuilder" :actionBegin="actionBegin" :dataSource="dataSource" :rule="importRules">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"/>
<e-column field="FirstName" label="First Name" type="string"/>
<e-column field="LastName" label="LastName" type="string"/>
<e-column field="Age" label="Age" type="number" :ruleTemplate='ageTemplate'/>
<e-column field='City' label='City' type='string' />
<e-column field='Country' label='Country' type='string' />
</e-columns>
</ejs-querybuilder>
</div>
</div>
</template>
<script setup>
import { QueryBuilderComponent as EjsQuerybuilder, ColumnDirective as EColumn, ColumnsDirective as EColumns } from '@syncfusion/ej2-vue-querybuilder';
import { SliderComponent } from "@syncfusion/ej2-vue-inputs";
import { DropDownListComponent } from "@syncfusion/ej2-vue-dropdowns";
import { getComponent } from '@syncfusion/ej2-base';
import { createApp} from 'vue';
const app = createApp({});
const rlTemplate = app.component('ruleTemplate', {
components: {
"ejs-dropdownlist": DropDownListComponent,
"ejs-slider": SliderComponent
},
template:
`<div class="e-rule e-rule-template">
<div class="e-rule-filter e-custom-filter">
<ejs-dropdownlist :change='fieldChange' :value="data.rule.field" :dataSource="data.columns" :fields="data.fields">
</ejs-dropdownlist>
</div>
<div>
<div class="e-slider-value">
<ejs-slider :min="min" :max="max" ref="slider" :ticks="rangeticks" :change="valueChange" :value="value" :id="valueID">
</ejs-slider>
</div>
<div class="e-rule-btn">
<button class="e-removerule e-rule-delete e-css e-btn e-small e-round">
<span class="e-btn-icon e-icons e-delete-icon"/>
</button>
</div>
</div>
</div>`,
data() {
return {
qryBldrObj: getComponent(document.getElementById('querybuilder'), 'query-builder'),
rangeticks: {
placement: 'Before',
largeStep: 5,
smallStep: 1,
showSmallTicks: true
},
min: 30,
max: 50
}
},
computed: {
valueID: function() {
return `${this.data.ruleID}_valuekey0`;
},
value: function() {
var num = 30;
if (this.data.rule.value !== '') {
num = this.data.rule.value;
}
return num;
}
},
methods: {
fieldChange: function(args) {
this.qryBldrObj.notifyChange(args.value, args.element, 'field');
},
valueChange: function(args) {
if (args.isInteracted) {
var elem = this.$refs.slider.$el;
this.qryBldrObj.notifyChange(args.value, elem, 'value');
}
}
}
});
const dataSource = employeeData;
const importRules = {
'condition': 'and',
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'greaterthanorequal',
'value': 30
}]
};
const ageTemplate = function(){
return {
template: rlTemplate
}
};
const actionBegin = (args) => {
args.rule.operator = 'greaterthanorequal';
if (args.requestType === 'template-initialize') {
if (args.rule.value === '') {
args.rule.value = 30;
}
}
if (args.requestType === 'template-create') {
getComponent(document.getElementById(args.ruleID + '_valuekey0'), 'slider').refresh();
}
};
var employeeData = [
{ 'EmployeeID': 1, 'FirstName': 'Nancy', 'Age': 31, 'City': 'Seattle', 'Country': 'USA' },
{ 'EmployeeID': 2, 'FirstName': 'Andrew', 'Age': 32, 'City': 'Tacoma', 'Country': 'USA' },
{ 'EmployeeID': 3, 'FirstName': 'Janet', 'Age': 33, 'City': 'Kirkland', 'Country': 'USA' },
{ 'EmployeeID': 4, 'FirstName': 'Margaret', 'Age': 33, 'City': 'Redmond', 'Country': 'USA' },
{ 'EmployeeID': 5, 'FirstName': 'Steven', 'Age': 34, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 6, 'FirstName': 'Michael', 'Age': 35, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 7, 'FirstName': 'Robert', 'Age': 36, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 8, 'FirstName': 'Laura', 'Age': 37, 'City': 'Seattle', 'Country': 'USA' },
{ 'EmployeeID': 9, 'FirstName': 'Anne', 'Age': 38, 'City': 'London', 'Country': 'UK' }
];
</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-splitbuttons/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-lists/styles/material.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material.css";
@import "../node_modules/@syncfusion/ej2-vue-querybuilder/styles/material.css";
.e-query-builder {
margin: 0 auto;
}
.e-rule-template {
padding-bottom: 12px;
}
.e-rule-btn {
float: right;
padding-top: 12px;
}
.e-rule-value-group {
margin: 12px;
border-top: 1px solid #e0e0e0;
min-height: 40px;
}
.e-query-builder .e-slider-container.e-horizontal {
padding: 0px 0 0 18px;
height: 0;
}
.e-query-builder .e-hide {
display: none;
}
.e-query-builder .e-custom-filter {
width: 40% !important;
}
.e-query-builder .e-slider-container.e-horizontal .e-slider {
top: calc(50% - 10px);
}
.e-query-builder .e-slider-value {
width: 40% !important;
padding: 12px 0 12px 0;
display: inline-block;
}
.e-rule-table {
margin: 20px 0 0 20px;
border: solid 1px #e0e0e0;
border-collapse: collapse;
font-family: Roboto;
width: 320px;
height: 180px;
overflow-y: auto;
}
.e-rule-table th,
.e-rule-table td {
border: solid #e0e0e0;
border-width: 1px 0 0;
display: table-cell;
font-size: 14px;
line-height: 20px;
overflow: hidden;
padding: 8px 21px;
vertical-align: middle;
white-space: nowrap;
width: auto;
}
</style>
<template>
<div class="control-section">
<div id="listbox-selection">
<ejs-querybuilder id="querybuilder" ref="querybuilder" :actionBegin="actionBegin" :dataSource="dataSource" :rule="importRules">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"/>
<e-column field="FirstName" label="First Name" type="string"/>
<e-column field="LastName" label="LastName" type="string"/>
<e-column field="Age" label="Age" type="number" :ruleTemplate='ageTemplate'/>
<e-column field='City' label='City' type='string' />
<e-column field='Country' label='Country' type='string' />
</e-columns>
</ejs-querybuilder>
</div>
</div>
</template>
<script>
import { QueryBuilderComponent, ColumnDirective, ColumnsDirective } from '@syncfusion/ej2-vue-querybuilder';
import { SliderComponent } from "@syncfusion/ej2-vue-inputs";
import { DropDownListComponent } from "@syncfusion/ej2-vue-dropdowns";
import { getComponent } from '@syncfusion/ej2-base';
import { createApp} from 'vue';
const app = createApp({});
const rlTemplate = app.component('ruleTemplate', {
components: {
"ejs-dropdownlist": DropDownListComponent,
"ejs-slider": SliderComponent
},
template:
`<div class="e-rule e-rule-template">
<div class="e-rule-filter e-custom-filter">
<ejs-dropdownlist :change='fieldChange' :value="data.rule.field" :dataSource="data.columns" :fields="data.fields">
</ejs-dropdownlist>
</div>
<div>
<div class="e-slider-value">
<ejs-slider :min="min" :max="max" ref="slider" :ticks="rangeticks" :change="valueChange" :value="value" :id="valueID">
</ejs-slider>
</div>
<div class="e-rule-btn">
<button class="e-removerule e-rule-delete e-css e-btn e-small e-round">
<span class="e-btn-icon e-icons e-delete-icon"/>
</button>
</div>
</div>
</div>`,
data() {
return {
qryBldrObj: getComponent(document.getElementById('querybuilder'), 'query-builder'),
rangeticks: {
placement: 'Before',
largeStep: 5,
smallStep: 1,
showSmallTicks: true
},
min: 30,
max: 50
}
},
computed: {
valueID: function() {
return `${this.data.ruleID}_valuekey0`;
},
value: function() {
var num = 30;
if (this.data.rule.value !== '') {
num = this.data.rule.value;
}
return num;
}
},
methods: {
fieldChange: function(args) {
this.qryBldrObj.notifyChange(args.value, args.element, 'field');
},
valueChange: function(args) {
if (args.isInteracted) {
var elem = this.$refs.slider.$el;
this.qryBldrObj.notifyChange(args.value, elem, 'value');
}
}
}
});
export default {
name: "App",
components: {
"ejs-querybuilder":QueryBuilderComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective
},
data: function() {
return {
dataSource: employeeData,
importRules: {
'condition': 'and',
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'greaterthanorequal',
'value': 30
}]
},
ageTemplate: () => {
return {
template : rlTemplate
}
}
};
},
methods: {
actionBegin: function(args) {
args.rule.operator = 'greaterthanorequal';
if (args.requestType === 'template-initialize') {
if (args.rule.value === '') {
args.rule.value = 30;
}
}
if (args.requestType === 'template-create') {
getComponent(document.getElementById(args.ruleID + '_valuekey0'), 'slider').refresh();
}
}
}
}
var employeeData = [
{ 'EmployeeID': 1, 'FirstName': 'Nancy', 'Age': 31, 'City': 'Seattle', 'Country': 'USA' },
{ 'EmployeeID': 2, 'FirstName': 'Andrew', 'Age': 32, 'City': 'Tacoma', 'Country': 'USA' },
{ 'EmployeeID': 3, 'FirstName': 'Janet', 'Age': 33, 'City': 'Kirkland', 'Country': 'USA' },
{ 'EmployeeID': 4, 'FirstName': 'Margaret', 'Age': 33, 'City': 'Redmond', 'Country': 'USA' },
{ 'EmployeeID': 5, 'FirstName': 'Steven', 'Age': 34, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 6, 'FirstName': 'Michael', 'Age': 35, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 7, 'FirstName': 'Robert', 'Age': 36, 'City': 'London', 'Country': 'UK' },
{ 'EmployeeID': 8, 'FirstName': 'Laura', 'Age': 37, 'City': 'Seattle', 'Country': 'USA' },
{ 'EmployeeID': 9, 'FirstName': 'Anne', 'Age': 38, 'City': 'London', 'Country': 'UK' }
];
</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-splitbuttons/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-lists/styles/material.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material.css";
@import "../node_modules/@syncfusion/ej2-vue-querybuilder/styles/material.css";
.e-query-builder {
margin: 0 auto;
}
.e-rule-template {
padding-bottom: 12px;
}
.e-rule-btn {
float: right;
padding-top: 12px;
}
.e-rule-value-group {
margin: 12px;
border-top: 1px solid #e0e0e0;
min-height: 40px;
}
.e-query-builder .e-slider-container.e-horizontal {
padding: 0px 0 0 18px;
height: 0;
}
.e-query-builder .e-hide {
display: none;
}
.e-query-builder .e-custom-filter {
width: 40% !important;
}
.e-query-builder .e-slider-container.e-horizontal .e-slider {
top: calc(50% - 10px);
}
.e-query-builder .e-slider-value {
width: 40% !important;
padding: 12px 0 12px 0;
display: inline-block;
}
.e-rule-table {
margin: 20px 0 0 20px;
border: solid 1px #e0e0e0;
border-collapse: collapse;
font-family: Roboto;
width: 320px;
height: 180px;
overflow-y: auto;
}
.e-rule-table th,
.e-rule-table td {
border: solid #e0e0e0;
border-width: 1px 0 0;
display: table-cell;
font-size: 14px;
line-height: 20px;
overflow: hidden;
padding: 8px 21px;
vertical-align: middle;
white-space: nowrap;
width: auto;
}
</style>