Templates in Vue Query builder component
16 Mar 202324 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>
import Vue from "vue";
import { QueryBuilderPlugin } from '@syncfusion/ej2-vue-querybuilder';
import { SliderPlugin } from "@syncfusion/ej2-vue-inputs";
import { DropDownListPlugin } from "@syncfusion/ej2-vue-dropdowns";
import { getComponent, compile,closest } from '@syncfusion/ej2-base';
import { DataManager, Predicate, Query } from '@syncfusion/ej2-data';
import { CheckBoxPlugin, ButtonPlugin } from "@syncfusion/ej2-vue-buttons";
import { DropDownButtonPlugin } from "@syncfusion/ej2-vue-splitbuttons";
Vue.use(QueryBuilderPlugin);
Vue.use(SliderPlugin);
Vue.use(DropDownListPlugin);
Vue.use(CheckBoxPlugin);
Vue.use(DropDownButtonPlugin);
Vue.use(ButtonPlugin);
export default {
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 : Vue.component('headerTemplate', {
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.native='btnClick'>
</ejs-button>
</div>`,
data(args) {
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'));
}
}
})
}
}
};
}
}
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>
import Vue from "vue";
import { QueryBuilderPlugin } from "@syncfusion/ej2-vue-querybuilder";
import { DropDownList } from '@syncfusion/ej2-dropdowns'
import { createElement, getComponent} from "@syncfusion/ej2-base";
let inOperators = ['in', 'notin'];
Vue.use(QueryBuilderPlugin);
export default {
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>
import Vue from "vue";
import { QueryBuilderPlugin } from "@syncfusion/ej2-vue-querybuilder";
import { RadioButtonPlugin } from "@syncfusion/ej2-vue-buttons";
import { DropDownList, MultiSelect, CheckBoxSelection } from '@syncfusion/ej2-dropdowns';
import { Slider, TextBox } from '@syncfusion/ej2-inputs';
import { CheckBox } from '@syncfusion/ej2-buttons';
import { createElement, getComponent, isNullOrUndefined } from "@syncfusion/ej2-base";
import { CheckBoxPlugin } from "@syncfusion/ej2-vue-buttons";
import { DropDownListPlugin } from "@syncfusion/ej2-vue-dropdowns";
MultiSelect.Inject(CheckBoxSelection);
Vue.use(QueryBuilderPlugin);
Vue.use(CheckBoxPlugin);
Vue.use(DropDownListPlugin);
export default {
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: Vue.component('paymentTemplate', {
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 as string, elem, 'value');
}
}
})
}
},
transactionTemplate: () => {
return{
template: Vue.component('transactionTemplate', {
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');
}
}
})
}
}
};
}
}
</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>
import Vue from "vue";
import { QueryBuilderPlugin } from '@syncfusion/ej2-vue-querybuilder';
import { SliderPlugin } from "@syncfusion/ej2-vue-inputs";
import { DropDownListPlugin } from "@syncfusion/ej2-vue-dropdowns";
import { getComponent, compile } from '@syncfusion/ej2-base';
import { DataManager, Predicate, Query } from '@syncfusion/ej2-data';
Vue.use(QueryBuilderPlugin);
Vue.use(SliderPlugin);
Vue.use(DropDownListPlugin);
export default {
data: function() {
return {
dataSource: employeeData,
importRules: {
'condition': 'and',
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'greaterthanorequal',
'value': 30
}]
},
ageTemplate: () => {
return {
template : Vue.component('ruleTemplate', {
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(args) {
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');
}
}
}
})
}
}
};
},
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>