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>