How to Customize the UI of the Angular Query Builder
Tags: angular, javascript, webdev
Published: 2021-03-12 12:29:35

---
title: How to Customize the UI of the Angular Query Builder
published: true
date: 2021-03-01 11:00:48 UTC
tags: angular, javascript, webdev
canonical_url: https://www.syncfusion.com/blogs/post/how-to-customize-the-ui-of-the-angular-query-builder.aspx
cover_image: https://www.syncfusion.com/blogs/wp-content/uploads/2021/03/How-to-Customize-the-UI-of-Angular-Query-Builder-672x372.png
---

Our Syncfusion [Angular Query Builder](https://www.syncfusion.com/angular-ui-components/angular-query-builder) is a graphical user interface component used to build queries. It supports data binding, templates, and importing and exporting queries from and to JSON and SQL formats.  Query Builder can be used to generate predicates that are used as conditions in DataManager. It can also auto-populate a data source and map it to appropriate fields from an array of JavaScript objects.

In this blog, I am going to walk you through the templating feature, which is used to customize the UI of the Angular Query Builder.

## Template

To provide flexibility in customization and a better user experience, the Angular Query Builder offers templates support. A template can include a mixture of static HTML and web controls. The following UI customization options are available:

- [Value template](#value-template): To customize the value column of a rule.
- [Rule template](#rule-template): To customize the entire rule.

### Value template

Users can customize the user interface to display the value column of a rule (condition) using the template property.

In the following sample code, the:

- **Dropdown List** component is used to render the custom transaction type field values.
- **Checkbox** component is used to render the custom payment mode field values.

####  app.component.html code

```html
<ejs-querybuilder id="querybuilder" #querybuilder [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" [template]="paymentTemplate">
    </e-column>
    <e-column field="TransactionType" label="Transaction Type" type="string" 
       [operators]="customOperators" [template]="transactionTemplate">
    </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>

<ng-template #paymentTemplate let-data>
  <ejs-dropdownlist [dataSource]='paymentDS' [value]='data.rule.value' 
     (change)="paymentChange($event, data.ruleID)">
  </ejs-dropdownlist>
</ng-template>
<ng-template #transactionTemplate let-data>
  <ejs-checkbox label='Is Expense' [checked]='data.rule.value === "Expense" ? 
     true: false' (change)="transactionChange($event, data.ruleID)">
  </ejs-checkbox>
</ng-template>
```

#### app.component.ts code

```js
import { Component, ViewChild, OnInit } from '@angular/core';
import { QueryBuilderComponent } from './../index';
import { ActionEventArgs, RuleModel } from '@syncfusion/ej2-querybuilder';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  @ViewChild('querybuilder') qryBldrObj: QueryBuilderComponent;
  public paymentDS: 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;
    elem = document.getElementById(ruleID).querySelector('.e-rule-value');
    this.qryBldrObj.notifyChange(
      e.checked === true ? 'Expense' : 'Income', elem, 'value');
  }

  paymentChange(e: any, ruleID: string): void {
    let elem: HTMLElement;
    elem = document.getElementById(ruleID).querySelector('.e-rule-value');
    this.qryBldrObj.notifyChange(e.value as string, elem, 'value');
  }
}
```

The following .gif image shows the UI customizations done in the Angular Query Builder using the Value Template property.  
 ![UI customizations done in the Angular Query Builder using the Value Template property](https://www.syncfusion.com/blogs/wp-content/uploads/2021/02/UI-customizations-done-in-the-Angular-Query-Builder-using-the-Value-Template-property.gif)

### Rule template

Users can also customize the user interface to display the entire rule using the **ruleTemplate** property.

Customize the Age field using the **ruleTemplate** property as shown in the following sample code. Here, set the default condition as **greater than or equal** and render the slider component for the value column. Finally, show the result using a table in the rule UI template.

#### app.component.html code

```html
<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" 
     [ruleTemplate]="ruleTemplate"></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>
```

#### Template code

```html
<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="{{data.ruleID}}_valuekey0" 
        (change)="valueChange($event, data.ruleID)"></ejs-slider>
      </div>
      <div class="e-rule-btn">
        <button id="{{data.ruleID}}_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-btn e-small e-round">
          <span class="e-btn-icon e-icons e-delete-icon"></span>
        </button>
      </div>
    </div>
    <div id="{{data.ruleID}}_section" class="e-rule-content e-hide">
      <table id="{{data.ruleID}}_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>
```

#### Customized CSS code

```css
ejs-querybuilder {
    display: block;
}
.e-rule-template {
    padding-bottom: 12px;
}
.e-query-builder .e-slider-container.e-horizontal {
    padding: 0px 0 0 18px;
    height: 0;
}
.e-query-builder .e-slider-container.e-horizontal .e-slider {
    top: calc(50% - 7px);
}
.e-query-builder .e-slide-val {
    width: 35%;
}
.e-rule-btn {
    float: right;
    padding-top: 12px;
}
.e-query-builder .e-hide {
    display: none;
}
.e-rule-content {
    margin: 0px 0 0 13px;
    height: 180px;
    width: 335px;
    overflow-y: auto;
    border: 1px solid #e0e0e0;
}
.e-rule-table {
    border-collapse: collapse;
}
.e-rule-table th,
.e-rule-table td {
    border: solid #e0e0e0;
    border-width: 1px 0 0;
    padding: 8px 21px;
}
```

#### app.component.ts code

```js
export class AppComponent implements OnInit {
  @ViewChild('querybuilder') qryBldrObj: QueryBuilderComponent;
  public actionArgs: ActionEventArgs;
  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') {
      this.actionArgs = args;
      args.rule.operator = 'greaterthanorequal';
      if (args.rule.value == '') {
        args.rule.value = 32;
      }
    }
  }
  fieldChange(e: any): void {
    this.qryBldrObj.notifyChange(e.value, e.element, 'field');
  };
  valueChange(e: any, ruleID: string): void {
    let elem: HTMLElement = document.getElementById(ruleID);
    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);
    let element: HTMLElement = document.getElementById(ruleID + '_section');
    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>${LastName}</td><td>${Age}</td></tr>';
    let compiledFunction: any = compile(template);
    let predicate: Predicate = this.qryBldrObj.getPredicate({condition: 'and', rules: [rule]});
    let dataManagerQuery: Query = new Query().select(['EmployeeID', 'LastName', 'Age']).where(predicate);
    let result: object[] = new DataManager(employeeData).executeLocal(dataManagerQuery);
    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'));
      });
    }
  }
}
```

The following .gif image shows all the UI customizations done in the Query Builder using the Rule Template property.  
 ![UI customizations done in the Angular Query Builder using the Rule Template property](https://www.syncfusion.com/blogs/wp-content/uploads/2021/02/UI-customizations-done-in-the-Query-Builder-using-the-Rule-Template-property.gif)

## Resources

You can find the projects in the following UG documentation links,

- [Angular Query Builder UI Customization using Value Template](https://ej2.syncfusion.com/angular/documentation/query-builder/columns/#template)
- [Angular Query Builder UI Customization using Rule Template](https://ej2.syncfusion.com/angular/documentation/query-builder/columns/#rule-template)

## Conclusion

I hope you now have a better understanding of the UI customization in the [Angular Query Builder](https://www.syncfusion.com/angular-ui-components/angular-query-builder) control. What else do you expect from our Query Builder? Please share your thoughts in the comments section.

Our Query Builder component is also available for the [Blazor](https://www.syncfusion.com/blazor-components/blazor-query-builder), ASP.NET ([Core](https://www.syncfusion.com/aspnet-core-ui-controls/query-builder), [MVC](https://www.syncfusion.com/aspnet-mvc-ui-controls/query-builder)), [JavaScript](https://www.syncfusion.com/javascript-ui-controls/js-query-builder), [React](https://www.syncfusion.com/react-ui-components/react-query-builder), and [Vue](https://www.syncfusion.com/vue-ui-components/vue-query-builder) platforms.

If you’re already a Syncfusion user, you can download the [product setup](https://www.syncfusion.com/account/downloads) to try out this control. Otherwise, you can download a free [30-day trial](https://www.syncfusion.com/downloads/fileformats/verify).

If you have any questions about these features, please contact us through our [support forum](https://www.syncfusion.com/forums), [Direct-Trac](https://www.syncfusion.com/support/directtrac/), or [feedback portal](https://www.syncfusion.com/feedback/angular). We are happy to assist you!

If you like this blog post, we think you’ll also like the following articles, too:

- [Top 5 Best Practices for Angular App Security](https://dev.to/syncfusion/top-5-best-practices-for-angular-app-security-3a7k) [Blog]
- [How to Integrate Third-Party Libraries into Angular Rich Text Editor](https://dev.to/syncfusion/how-to-integrate-third-party-libraries-into-angular-rich-text-editor-o2m) [Blog]
- [A Complete Guide to Running a Full-Stack Angular Application in a Monorepo](https://dev.to/syncfusion/a-complete-guide-to-running-a-full-stack-angular-application-in-a-monorepo-kbg) [Blog]
- [Angular Succinctly](https://www.syncfusion.com/ebooks/angular-succinctly) [Ebook]