Template in Angular Query builder component

8 Feb 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 { 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({
  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 { 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 { AppComponent } from './app.component'
import { DropDownButtonModule } from '@syncfusion/ej2-angular-splitbuttons';

/**
 * Module
 */

@NgModule({
  imports: [
    BrowserModule,
	  QueryBuilderModule,
    CheckBoxModule,
    DropDownListModule,
    DropDownButtonModule
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

import 'zone.js';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
<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 { 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({
    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 { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder';
import { AppComponent } from './app.component'
import { enableRipple } from '@syncfusion/ej2-base';

enableRipple(true);

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        QueryBuilderModule,
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

import 'zone.js';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);

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 { Component, ViewChild, OnInit } from '@angular/core';
import { QueryBuilderComponent } from '@syncfusion/ej2-angular-querybuilder';
import { RuleModel } from '@syncfusion/ej2-querybuilder';
@Component({
    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');
  }
}
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 { AppComponent } from './app.component'
import { enableRipple } from '@syncfusion/ej2-base';

enableRipple(true);

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        QueryBuilderModule,
		CheckBoxModule,
		DropDownListModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

import 'zone.js';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);

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 { 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({
    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 { 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 { AppComponent } from './app.component'
import { enableRipple } from '@syncfusion/ej2-base';

enableRipple(true);

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        QueryBuilderModule,
		SliderModule,
		DropDownListModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

import 'zone.js';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
<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>