Template in Angular Query builder component
5 Dec 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 using ngTemplate
and assign the values when requestType is header-template-create in actionBegin
event.
The #headerTemplate
template variable identifies the NgTemplate content as the header.
In the following sample dropdown, splitbutton and button are used as the custom components in the header.
import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder'
import { DropDownListModule } from '@syncfusion/ej2-angular-dropdowns'
import { CheckBoxModule } from '@syncfusion/ej2-angular-buttons'
import { DropDownButtonModule } from '@syncfusion/ej2-angular-splitbuttons'
import { CommonModule } from '@angular/common';
import { Component, ViewChild, OnInit } from '@angular/core';
import { QueryBuilderComponent } from '@syncfusion/ej2-angular-querybuilder';
import { ActionEventArgs, RuleModel } from '@syncfusion/ej2-querybuilder';
import { ItemModel, MenuEventArgs } from '@syncfusion/ej2-splitbuttons';
import { closest } from '@syncfusion/ej2-base';
@Component({
imports: [
CommonModule,
QueryBuilderModule,
CheckBoxModule,
DropDownListModule,
DropDownButtonModule
],
standalone: true,
selector: 'app-root',
templateUrl: `template-driven.html`
})
export class AppComponent implements OnInit {
@ViewChild('querybuilder') qryBldrObj: QueryBuilderComponent | undefined;
public ds: { [key: string]: Object}[] = [{'key': 'AND', 'value': 'and'},{'key': 'OR', 'value': 'or'}];
public ddbitems?: ItemModel[];
public importRules?: RuleModel;
public actionArgs?: ActionEventArgs;
public deleteGroupBtn?: Element;
public fields?: Object;
ngOnInit(): void {
this.importRules = {
'condition': 'and', 'not': true,
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'equal',
'value': 34
},
{
'label': 'LastName',
'field': 'LastName',
'type': 'string',
'operator': 'equal',
'value': 'vinit'
},
{
'condition': 'or',
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'equal',
'value': 34
}]
}]
};
this.ddbitems = [
{
text: 'AddGroup',
iconCss: 'e-icons e-add-icon e-addgroup'
},
{
text: 'AddCondition',
iconCss: 'e-icons e-add-icon e-addrule'
}];
this.fields = { text: 'key', value: 'value' };
}
onChange(e: any): void {
this.qryBldrObj!.notifyChange(e.checked,e.event.target, 'not');
}
conditionChange(e: any): void {
this.qryBldrObj!.notifyChange(e.value, e.element, 'condition');
}
onSelect(event: MenuEventArgs): void {
let addbtn: Element = closest(event.element,'.e-dropdown-popup'); let ddbId: string = addbtn.id;
let ddb: string[]= 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]);
}
}
onClick(e: any): void {
this.qryBldrObj!.deleteGroup(closest(e.target.offsetParent, '.e-group-container'));
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<ejs-querybuilder id="querybuilder" #querybuilder width="100%" [rule] = "importRules" enableNotCondition = true>
<e-columns>
<e-column field="EmployeeID" label="EmployeeID" type="number"></e-column>
<e-column field="FirstName" label="FirstName" type="string"></e-column>
<e-column field="LastName" label="LastName" type="string"></e-column>
<e-column field="Age" label="Age" type="number"></e-column>
<e-column field="City" label="City" type="string"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
<ng-template #headerTemplate let-data>
<div class = "e-groupheader">
<button *ngIf="data.notCondition !== undefined" class='e-cb-wrapper'>
<ejs-checkbox id ="{{data.ruleID}}_notOption" label='not' [checked]='data.notCondition' (change)="onChange($event)">
</ejs-checkbox> </button>
<ejs-dropdownlist id ="{{data.ruleID}}_cndtn" [dataSource]='ds' [value]='data.condition' [fields]='fields' cssClass="e-custom-group-btn" (change)="conditionChange($event)">
</ejs-dropdownlist>
<button ejs-dropdownbutton id="{{data.ruleID}}_addbtn" [items]='ddbitems' cssClass= "e-round e-small e-caret-hide e-addrulegroup e-add-btn" iconCss="e-icons e-add-icon" (select)="onSelect($event)"></button>
<button ejs-button *ngIf ="data.ruleID !== 'querybuilder_group0'" id= '{{data.ruleID}}_dltbtn' class= "e-btn e-delete-btn e-lib e-small e-round e-icon-btn" (click)="onClick($event)">
<span class = 'e-btn-icon e-icons e-delete-icon'></span>
</button>
</div>
</ng-template>
</ejs-querybuilder>
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.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder'
import { enableRipple } from '@syncfusion/ej2-base'
import { Component, ViewChild, OnInit } from '@angular/core';
import { RuleModel, QueryBuilderComponent, ColumnsModel, TemplateColumn } from '@syncfusion/ej2-angular-querybuilder';
import { expenseData } from './datasource';
import { DropDownList, MultiSelect } from '@syncfusion/ej2-dropdowns';
import { getComponent, createElement } from '@syncfusion/ej2-base';
@Component({
imports: [
QueryBuilderModule,
],
standalone: true,
selector: 'app-root',
template: `<!-- To render Query Builder. -->
<ejs-querybuilder #querybuilder width="70%" [dataSource]="data" [rule]="importRules" [columns]="filter">
</ejs-querybuilder>`
})
export class AppComponent implements OnInit {
public data?: Object[];
public importRules?: RuleModel;
@ViewChild('querybuilder')
public qryBldrObj?: QueryBuilderComponent;
public filter?: ColumnsModel[];
public paymentTemplate?: TemplateColumn;
public inOperators: string[] = ['in', 'notin'];
ngOnInit(): void {
this.data = expenseData;
this.paymentTemplate = {
create: () => {
return createElement('input', { attrs: { 'type': 'text' } });
},
destroy: (args: { elementId: string }) => {
let multiSelect: MultiSelect = (getComponent(document.getElementById(args.elementId) as any, 'multiselect') as MultiSelect);
if (multiSelect) {
multiSelect.destroy();
}
let dropdown: DropDownList = (getComponent(document.getElementById(args.elementId) as any, 'dropdownlist') as DropDownList);
if (dropdown) {
dropdown.destroy();
}
},
write: (args: { elements: Element, values: string[] | string, operator: string }) => {
let ds: string[] = ['Cash', 'Debit Card', 'Credit Card', 'Net Banking', 'Wallet'];
if (this.inOperators.indexOf(args.operator) > -1) {
let multiSelectObj: MultiSelect = new MultiSelect({
dataSource: ds,
value: args.values as string[],
mode: 'CheckBox',
placeholder: 'Select Transaction',
change: (e: any) => {
this.qryBldrObj!.notifyChange(e.value, e.element);
}
});
multiSelectObj.appendTo('#' + args.elements.id);
} else {
let dropDownObj: DropDownList = new DropDownList({
dataSource: ds,
value: args.values as string,
change: (e: any) => {
this.qryBldrObj!.notifyChange(e.itemData.value, e.element);
}
});
dropDownObj.appendTo('#' + args.elements.id);
}
}
};
this.filter = [
{ field: 'Category', label: 'Category', type: 'string' },
{ field: 'PaymentMode', label: 'Payment Mode', type: 'string', template: this.paymentTemplate },
{ field: 'TransactionType', label: 'Transaction Type', type: 'string' },
{ field: 'Description', label: 'Description', type: 'string' },
{ field: 'Date', label: 'Date', type: 'date' },
{ field: 'Amount', label: 'Amount', type: 'number'}
];
this.importRules = {
'condition': 'and',
'rules': [{
'label': 'Payment Mode',
'field': 'PaymentMode',
'type': 'string',
'operator': 'equal',
'value': 'Cash'
}]
};
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Using Template
The value template for a particular column can be specified using the content of the NgTemplate. The #template
template variable identifies the NgTemplate content as the corresponding column.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder'
import { CheckBoxModule } from '@syncfusion/ej2-angular-buttons'
import { DropDownListModule } from '@syncfusion/ej2-angular-dropdowns'
import { enableRipple } from '@syncfusion/ej2-base'
import { Component, ViewChild, OnInit } from '@angular/core';
import { QueryBuilderComponent } from '@syncfusion/ej2-angular-querybuilder';
import { RuleModel } from '@syncfusion/ej2-querybuilder';
@Component({
imports: [
QueryBuilderModule,
CheckBoxModule,
DropDownListModule
],
standalone: true,
selector: 'app-root',
templateUrl: `template-driven.html`
})
export class AppComponent implements OnInit {
@ViewChild('querybuilder') qryBldrObj: QueryBuilderComponent | undefined;
public paymentDataSource: string[] = ['Cash', 'Debit Card', 'Credit Card', 'Net Banking'];
public importRules?: RuleModel;
public customOperators?: any;
ngOnInit(): void {
this.importRules = {
'condition': 'and',
'rules': [{
'label': 'Transaction Type',
'field': 'TransactionType',
'type': 'string',
'operator': 'equal',
'value': 'Expense'
},
{
'label': 'Payment Mode',
'field': 'PaymentMode',
'type': 'string',
'operator': 'equal',
'value': 'Cash'
}]
};
this.customOperators = [
{value: 'equal', key: 'Equal'},
{value: 'notequal', key: 'Not Equal'}
];
}
transactionChange(e: any, ruleID: string): void {
let elem: HTMLElement = document.getElementById(ruleID)!.querySelector('.e-rule-value') as HTMLElement;
this.qryBldrObj!.notifyChange(e.checked === true ? 'Expense' : 'Income', elem, 'value');
}
paymentChange(e: any, ruleID: string): void {
let elem: HTMLElement = document.getElementById(ruleID)!.querySelector('.e-rule-value') as HTMLElement;
this.qryBldrObj!.notifyChange(e.value as string, elem, 'value');
}
}
<ejs-querybuilder id="querybuilder" #querybuilder width="100%" [rule] = "importRules">
<e-columns>
<e-column field="Category" label="Category" type="string"></e-column>
<e-column field="PaymentMode" label="Payment Mode" type="string" [operators]="customOperators">
<ng-template #template let-data>
<ejs-dropdownlist [dataSource]='paymentDataSource' [value]='data.rule.value' (change)="paymentChange($event, data.ruleID)">
</ejs-dropdownlist>
</ng-template>
</e-column>
<e-column field="TransactionType" label="Transaction Type" type="string" [operators]="customOperators">
<ng-template #template let-data>
<ejs-checkbox label='Is Expense' [checked]='data.rule.value === "Expense" ? true: false' (change)="transactionChange($event, data.ruleID)">
</ejs-checkbox>
</ng-template>
</e-column>
<e-column field="Description" label="Description" type="string"></e-column>
<e-column field="Date" label="Date" type="string"></e-column>
<e-column field="Amount" label="Amount" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
Rule Template
Rule Template allows to define your own user interface for columns. To implement ruleTemplate
you can create the user interface using ngTemplate
and assign the values through actionBegin
event.
The #ruleTemplate
template variable identifies the NgTemplate content as the corresponding column.
In the following sample, dropdown and slider are used as the custom component and applied greaterthanorequal
operator to Age
column.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder'
import { DropDownListModule } from '@syncfusion/ej2-angular-dropdowns'
import { SliderModule } from '@syncfusion/ej2-angular-inputs'
import { enableRipple } from '@syncfusion/ej2-base'
import { CommonModule } from '@angular/common';
import { Component, ViewChild, OnInit } from '@angular/core';
import { QueryBuilderComponent } from '@syncfusion/ej2-angular-querybuilder';
import { ActionEventArgs, RuleModel } from '@syncfusion/ej2-querybuilder';
import { compile } from '@syncfusion/ej2-base';
import { DataManager, Predicate, Query } from '@syncfusion/ej2-data';
import { employeeData } from './datasource';
@Component({
imports: [
CommonModule,
QueryBuilderModule,
SliderModule,
DropDownListModule
],
standalone: true,
selector: 'app-root',
templateUrl: `template-driven.html`
})
export class AppComponent implements OnInit {
@ViewChild('querybuilder') qryBldrObj: QueryBuilderComponent | undefined;
public importRules?: RuleModel;
public rangeticks?: Object;
ngOnInit(): void {
this.importRules = {
'condition': 'and',
'rules': [{
'label': 'Age',
'field': 'Age',
'type': 'number',
'operator': 'greaterthanorequal',
'value': 32
}]
};
this.rangeticks = { placement: 'Before', largeStep: 5, smallStep: 1, showSmallTicks: true };
}
actionBegin(args: ActionEventArgs): void {
if (args.requestType === 'template-initialize') {
args.rule!.operator = 'greaterthanorequal';
if (args.rule!.value === '') {
args.rule!.value = 30;
}
}
}
fieldChange(e: any): void {
this.qryBldrObj!.notifyChange(e.value, e.element, 'field');
};
valueChange(e: any, ruleID: string): void {
let elem: HTMLElement = document.getElementById(ruleID) as HTMLElement;
this.qryBldrObj!.notifyChange(e.value as Date, elem, 'value');
this.refreshTable(this.qryBldrObj!.getRule(elem), ruleID);
}
viewDetails(ruleID: string): void {
let ruleElem: HTMLElement = document.getElementById(ruleID) as HTMLElement;
let element: HTMLElement = document.getElementById(ruleID + '_section') as HTMLElement;
if (element.className.indexOf('e-hide') > -1) {
this.refreshTable(this.qryBldrObj!.getRule(ruleElem), ruleID);
element.className = element.className.replace('e-hide', '');
document.getElementById(ruleID + '_option')!.querySelector('.e-content')!.textContent = 'Hide Details';
} else {
element.className += ' e-hide';
document.getElementById(ruleID + '_option')!.querySelector('.e-content')!.textContent = 'View Details';
}
}
refreshTable(rule: RuleModel, ruleID: string): void {
let template: string = '<tr><td>${EmployeeID}</td><td>${FirstName}</td><td>${Age}</td></tr>';
let compiledFunction: any = compile(template);
let dataManagerQuery: Query = this.qryBldrObj!.getDataManagerQuery({condition: 'and', rules: [rule]});
let dataManager: DataManager = new DataManager(employeeData);
dataManager.defaultQuery = dataManagerQuery;
let result: object[] = dataManager.executeLocal();
let table: HTMLElement = document.getElementById(ruleID + '_datatable') as HTMLElement;
if (table) {
if (result.length) {
table.style.display = 'block';
} else {
table.style.display = 'none';
}
table.querySelector('tbody')!.innerHTML = '';
result.forEach((data) => {
table.querySelector('tbody')!.appendChild(compiledFunction(data)[0].querySelector('tr'));
});
}
}
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
<ejs-querybuilder id="querybuilder" #querybuilder width="100%" [rule] = "importRules" (actionBegin)="actionBegin($event)">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"></e-column>
<e-column field="FirstName" label="First Name" type="string"></e-column>
<e-column field="LastName" label="LastName" type="string"></e-column>
<e-column field="Age" label="Age" type="number">
<ng-template #ruleTemplate let-data>
<div class="e-rule e-rule-template">
<div class="e-rule-header">
<div class="e-rule-filter">
<ejs-dropdownlist (change)="fieldChange($event)" [fields]="data.fields" [dataSource]="data.columns" [value]="data.rule.field">
</ejs-dropdownlist>
</div>
<div *ngIf="data.rule.type ==='number'" class="e-rule-value e-slide-val">
<ejs-slider [value]='data.rule.value' [ticks]='rangeticks' min=30 max=50 id = "_valuekey0" (change)="valueChange($event, data.ruleID)">
</ejs-slider>
</div>
<div class="e-rule-btn">
<button id="_option" (click)="viewDetails(data.ruleID)" class="e-primary e-btn e-small">
<span class='e-content'>View Details</span>
</button>
<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"></span>
</button>
</div>
</div>
<div id="_section" class="e-rule-content e-hide">
<table id="_datatable" class='e-rule-table e-hide'>
<thead> <tr><th>EmployeeID</th><th>FirstName</th><th>Age</th></tr></thead>
<tbody></tbody>
</table>
</div>
</div>
</ng-template>
</e-column>
<e-column field="City" label="City" type="string"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>