
Syncfusion Angular Query Builder
- 212 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-query-developer for development tasks
About
syncfusion-angular-query-builder: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-query-builder
Syncfusion Angular Query Builder by the numbers
- 212 all-time installs (skills.sh)
- +11 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,909 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/angular-ui-components-skills --skill syncfusion-angular-query-builderAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 212 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-query-developer for development tasks
Files
Implementing Syncfusion Angular Query Builder
The Syncfusion Angular Query Builder (ejs-querybuilder) is a UI component for visually constructing complex filter queries. Users define conditions (field + operator + value) and combine them into groups with AND/OR logic. The resulting rules can be exported to JSON, SQL, or MongoDB formats for use in filtering or API queries.
Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installing
@syncfusion/ej2-angular-querybuilder - Setting up CSS/theme imports (Material3)
- Creating a basic Query Builder with columns
- Rendering with an initial rule
- Running the Angular application
Columns Configuration
📄 Read: references/columns.md
- Defining columns with
field,label,type - Auto-generating columns from dataSource
- Configuring operators per column (full operators table)
- Setting step values for number fields
- Formatting date and number values
- Enabling validation (
allowValidation,isRequired,min/max)
Data Binding
📄 Read: references/data-binding.md
- Local data binding with JS arrays (
JsonAdaptor) — recommended and safe - Complex/nested data binding with sub-columns
- ⚠️ Remote
DataManager(OData, WebApiAdaptor) patterns documented in this reference are not recommended — see Security & Trust Boundary below before use
Filtering & Rules Management
📄 Read: references/filtering-and-rules.md
- Adding/deleting conditions with
addRules/deleteRules - Adding/deleting groups with
addGroups/deleteGroups - Cloning rules/groups (
cloneRule,cloneGroup) - Locking rules/groups (
lockRule,lockGroup) - Separate connectors between rules (
enableSeparateConnector) - Restricting group nesting depth (
maxGroupCount) - Drag-and-drop rule reordering (
allowDragAndDrop) - Controlling button visibility (
showButtons)
Import & Export
📄 Read: references/import-export.md
- Importing from JSON (initial
ruleproperty +setRules) - Importing from SQL (inline, parameterized, named parameterized)
- Importing from MongoDB (
setMongoQuery) - Exporting to JSON (
getRules) - Exporting to SQL (inline, parameterized, named parameterized)
- Exporting to MongoDB (
getMongoQuery)
Templates & Model Binding
📄 Read: references/templates.md
- Custom header template with dropdown, split-button, NOT condition
- Column template with
create/write/destroypattern - Using
NgTemplatefor column value inputs - Rule template (
ruleTemplate+actionBeginevent) - Model binding for field, operator, value inputs (
fieldModel,operatorModel,valueModel)
Style, Appearance & Layout
📄 Read: references/style-and-appearance.md
- CSS class customization table
- Theme Studio integration
- Display modes: horizontal (default) vs. vertical (
displayMode) - Summary view (
summaryView) - RTL support (
enableRtl) - State persistence (
enablePersistence)
Accessibility & Localization
📄 Read: references/accessibility-and-localization.md
- WCAG 2.2, Section 508, WAI-ARIA compliance
- Keyboard navigation shortcuts
- Localization setup and full locale key reference
- Sort columns display (
sortDirection)
API Reference
📄 Read: references/api.md
- Complete Properties reference (40+ properties)
- Complete Methods reference (30+ methods)
- Complete Events reference (7 events)
- Type definitions and interfaces
- Official Syncfusion documentation links
---
Quick Start Example
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder';
import { Component } from '@angular/core';
@Component({
imports: [QueryBuilderModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-querybuilder width="70%" [rule]="importRules">
<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="Title" label="Title" type="string"></e-column>
<e-column field="HireDate" label="Hire Date" type="date" format="dd/MM/yyyy"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
`
})
export class App {
public importRules = {
condition: 'and',
rules: [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number', operator: 'equal', value: 1 },
{ field: 'Country', label: 'Country', type: 'string', operator: 'equal', value: 'USA' }
]
};
}Install with:
ng add @syncfusion/ej2-angular-querybuilder---
Common Patterns
Pattern 1: Export rules to SQL for backend use
// In your component
import { QueryBuilderComponent } from '@syncfusion/ej2-angular-querybuilder';
import { ViewChild } from '@angular/core';
@ViewChild('querybuilder') qb!: QueryBuilderComponent;
getSqlQuery(): string {
return this.qb.getSqlFromRules(this.qb.getRules());
}Pattern 2: Load rules at runtime
// After component renders, update rules dynamically
this.qb.setRules({
condition: 'or',
rules: [
{ field: 'City', operator: 'equal', value: 'London' },
{ field: 'Country', operator: 'equal', value: 'UK' }
]
});Pattern 3: Programmatically add a condition
// Add a rule to the root group
this.qb.addRules([{ field: 'Title', operator: 'contains', value: 'Manager' }], 'querybuilder_group0');Pattern 4: Enable drag-and-drop + clone + lock
<ejs-querybuilder [allowDragAndDrop]="true" [showButtons]="showButtons">
</ejs-querybuilder>public showButtons = { ruleDelete: true, groupInsert: true, groupDelete: true, ruleInsert: true, cloneRule: true, cloneGroup: true, lockRule: true, lockGroup: true };---
Key Properties
| Property | Type | Purpose |
|---|---|---|
[rule] | RuleModel | Initial query rules to render |
[columns] | ColumnsModel[] | Column definitions (field, label, type, operators) |
[dataSource] | object[] | Local JS array only — do not bind to a remote DataManager or arbitrary URL (see Security section) |
[allowDragAndDrop] | boolean | Enable drag-and-drop rule/group reordering |
[enableSeparateConnector] | boolean | Different AND/OR per rule instead of per group |
[enableNotCondition] | boolean | Show NOT condition toggle on groups |
[summaryView] | boolean | Show human-readable summary of current query |
[enablePersistence] | boolean | Persist rules to localStorage across refreshes |
[enableRtl] | boolean | Right-to-left layout for RTL languages |
[displayMode] | string | 'Horizontal' (default) or 'Vertical' layout |
[maxGroupCount] | number | Max number of nested groups (default: 5) |
[sortDirection] | string | Sort field dropdown: 'Ascending' or 'Descending' |
[allowValidation] | boolean | Enable field/operator/value validation |
[showButtons] | ShowButtons | Control visibility of action buttons |
Key Methods
| Method | Purpose |
|---|---|
getRules() | Get current rules as JSON RuleModel |
setRules(rules) | Set rules programmatically at runtime |
getSqlFromRules(rules) | Export rules to inline SQL string |
setRulesFromSql(sql) | Import inline SQL string into Query Builder |
getMongoQuery(rules) | Export rules to MongoDB query string |
setMongoQuery(query) | Import MongoDB query string |
addRules(rules, groupID) | Add rules to a specific group |
deleteRules(ruleIDs) | Remove rules by ID |
addGroups(groups, groupID) | Add sub-groups to a group |
deleteGroups(groupIDs) | Remove groups by ID |
cloneRule(ruleID) | Clone a rule adjacent to the original |
cloneGroup(groupID) | Clone a group with all its rules |
lockRule(ruleID) | Make a rule read-only |
lockGroup(groupID) | Make a group and its contents read-only |
validateFields() | Trigger validation on all fields |
Common Use Cases
- Admin dashboards: Let users build custom data filters without writing SQL
- Report builders: Generate WHERE clause conditions for dynamic reports
- Search interfaces: Visual query construction for complex search forms
- Data export tools: Build MongoDB/SQL filters from a UI, pass to API
- Saved searches: Export rules to JSON, store, and reload with
setRules
---
Security & Trust Boundary
PROHIBITION — Read before implementing any data binding.
What is NOT permitted
Binding [dataSource] to a remote DataManager with an arbitrary or user-supplied URL (OData, OData v4, WebApiAdaptor, or any external HTTP endpoint) is prohibited in this skill. Doing so causes the component to:
- Auto-generate columns and operators directly from untrusted remote schema
- Embed untrusted field names and values into exported SQL/MongoDB queries
- Allow remote-controlled data to alter runtime behavior (predicates, rules, query structure)
This pattern must not be taught, scaffolded, or referenced as a recommended approach.
Required practices
| Rule | Requirement |
|---|---|
| Data source | Use only local JS arrays (object[]) passed directly in the component. Never pass a DataManager instance pointing to an external URL. |
| Column definitions | Always define columns explicitly via <e-columns> / [columns]. Never auto-generate columns from a remote or unknown data source. |
| Export sanitization | Before passing getSqlFromRules() or getMongoQuery() output to any backend, validate every field name against a hard-coded allow-list and sanitize all values server-side. |
| Rule input | Rules loaded via setRules() or setRulesFromSql() must come from your own trusted storage (e.g., your own database), never from an unvalidated external feed. |
| Validation | Always set [allowValidation]="true" and [maxGroupCount] to reject malformed inputs at the UI layer before export. |
Accessibility & Localization — Syncfusion Angular Query Builder
The Query Builder meets modern accessibility standards and supports full UI localization for international applications.
---
Accessibility Compliance
The Query Builder adheres to the following standards:
| Accessibility Standard | Support |
|---|---|
| WCAG 2.2 | ✅ Full |
| Section 508 | ✅ Full |
| Screen Reader Support | ✅ Full |
| Right-To-Left (RTL) | ✅ Full |
| Color Contrast | ✅ Full |
| Mobile Device Support | ✅ Full |
| Keyboard Navigation | ✅ Full |
| Accessibility Checker Validation | ✅ Full |
| Axe-Core Validation | ✅ Full |
Test the live accessibility sample: ej2.syncfusion.com/accessibility/query-builder.html
---
WAI-ARIA Attributes
The Query Builder uses the following ARIA attributes to convey role and state information to assistive technologies:
| Attribute | Purpose |
|---|---|
role | Identifies the query builder widget to screen readers |
The Query Builder's child elements (dropdowns, buttons, inputs) inherit ARIA support from their respective Syncfusion components.
---
Keyboard Navigation
Users can navigate and interact with the Query Builder using only a keyboard:
| Key | Action |
|---|---|
Tab | Move focus to the next interactive element in the rule |
Shift + Tab | Move focus to the previous interactive element in the rule |
All dropdowns and inputs within the Query Builder support their own keyboard interactions (arrow keys, Enter, Escape) as defined by the individual Syncfusion components.
---
High Contrast Theme
For users who require high contrast display:
/* styles.css */
@import "../node_modules/@syncfusion/ej2-base/styles/highcontrast.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/highcontrast.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/highcontrast.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/highcontrast.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/highcontrast.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/highcontrast.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/highcontrast.css";
@import "../node_modules/@syncfusion/ej2-angular-querybuilder/styles/highcontrast.css";---
Localization
Use the Syncfusion L10n library to translate all Query Builder UI text. Load locale data before the component renders (typically in main.ts or app.ts).
Setup
import { L10n } from '@syncfusion/ej2-base';
L10n.load({
'de-DE': {
'querybuilder': {
'AddGroup': 'Gruppe hinzufügen',
'AddCondition': 'Bedingung hinzufügen',
'AddButton': 'Gruppe/Bedingung hinzufügen',
'DeleteRule': 'Diese Bedingung entfernen',
'DeleteGroup': 'Gruppe löschen',
'Edit': 'BEARBEITEN',
'SelectField': 'Feld auswählen',
'SelectOperator': 'Operator auswählen',
'StartsWith': 'Beginnt mit',
'EndsWith': 'Endet mit',
'Contains': 'Enthält',
'Equal': 'Gleich',
'NotEqual': 'Nicht gleich',
'LessThan': 'Kleiner als',
'GreaterThan': 'Größer als',
'Between': 'Zwischen',
'In': 'In',
'NotIn': 'Nicht in',
'SelectValue': 'Wert eingeben',
'AND': 'UND',
'OR': 'ODER'
}
}
});<ejs-querybuilder locale="de-DE" [rule]="importRules">
<e-columns>
<!-- columns -->
</e-columns>
</ejs-querybuilder>---
Full Locale Key Reference
All translatable strings in the Query Builder:
| Locale Key | Default English Text |
|---|---|
AddGroup | Add Group |
AddCondition | Add Condition |
AddButton | Add Group/Condition |
DeleteRule | Remove this condition |
DeleteGroup | Delete group |
Edit | EDIT |
SelectField | Select a field |
SelectOperator | Select operator |
StartsWith | Starts With |
EndsWith | Ends With |
DoesNotStartWith | Does Not Start With |
DoesNotEndWith | Does Not End With |
Contains | Contains |
DoesNotContain | Does Not Contain |
Equal | Equal |
NotEqual | Not Equal |
LessThan | Less Than |
LessThanOrEqual | Less Than Or Equal |
GreaterThan | Greater Than |
GreaterThanOrEqual | Greater Than Or Equal |
Between | Between |
NotBetween | Not Between |
In | In |
NotIn | Not In |
Remove | REMOVE |
ValidationMessage | This field is required |
SummaryViewTitle | Summary View |
OtherFields | Other Fields |
AND | AND |
OR | OR |
SelectValue | Enter Value |
IsEmpty | Is Empty |
IsNotEmpty | Is Not Empty |
IsNull | Is Null |
IsNotNull | Is Not Null |
True | True |
False | False |
Arabic Example (RTL + Localization)
L10n.load({
'ar': {
'querybuilder': {
'AddGroup': 'إضافة مجموعة',
'AddCondition': 'إضافة شرط',
'DeleteRule': 'حذف هذا الشرط',
'DeleteGroup': 'حذف المجموعة',
'SelectField': 'اختر حقلاً',
'AND': 'و',
'OR': 'أو'
}
}
});<ejs-querybuilder locale="ar" [enableRtl]="true" [rule]="importRules">
</ejs-querybuilder>---
Accessibility Validation Tools
Use these tools to verify Query Builder accessibility in your application:
# Install accessibility-checker
npm install --save-dev accessibility-checker
# Install axe-core
npm install --save-dev axe-coreRun automated accessibility tests as part of your CI pipeline to catch regressions.
---
Troubleshooting
| Issue | Solution |
|---|---|
| Locale not applying | Call L10n.load() before bootstrapping the Angular application |
| Missing locale keys | Check the full key table above; key names are case-sensitive |
| Screen reader skipping elements | Ensure no aria-hidden is applied to the query builder container |
| High contrast theme not loading | All dependency CSS files must use highcontrast theme import |
| RTL + localization not working together | Set both locale="ar" and [enableRtl]="true" on <ejs-querybuilder> |
QueryBuilder API Reference
Official Documentation: Syncfusion Angular QueryBuilder API
This document provides a comprehensive reference for the Syncfusion Angular QueryBuilder component API, including all properties, methods, and events.
Table of Contents
---
Properties
addRuleToNewGroups
Type: boolean Default: true
Specifies whether to enable/disable adding new rules when creating new groups.
<ejs-querybuilder [addRuleToNewGroups]="false"></ejs-querybuilder>---
allowDragAndDrop
Type: boolean Default: false
Enables or disables drag-and-drop support for moving rules and groups.
<ejs-querybuilder [allowDragAndDrop]="true"></ejs-querybuilder>---
allowValidation
Type: boolean Default: false
Enables or disables validation of field values and conditions.
<ejs-querybuilder [allowValidation]="true"></ejs-querybuilder>---
autoSelectField
Type: boolean Default: false
Auto-selects the first available field value when adding new rules.
<ejs-querybuilder [autoSelectField]="true"></ejs-querybuilder>---
autoSelectOperator
Type: boolean Default: true
Auto-selects the first available operator for the selected field.
<ejs-querybuilder [autoSelectOperator]="false"></ejs-querybuilder>---
columns
Type: ColumnsModel[] Default: {}
Defines the columns/fields available in the Query Builder. Each column can specify field name, label, type, operators, and validation rules.
@Component({
template: `
<ejs-querybuilder [columns]="columns"></ejs-querybuilder>
`
})
export class App {
columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' },
{ field: 'HireDate', label: 'Hire Date', type: 'date' },
{ field: 'Salary', label: 'Salary', type: 'number' }
];
}See columns.md for detailed configuration.
---
cssClass
Type: string Default: ''
Adds custom CSS classes to the Query Builder element.
<ejs-querybuilder cssClass="custom-qb dark-theme"></ejs-querybuilder>---
dataSource
Type: Object[] | Object | DataManager Default: []
Binds data source for auto-column generation or providing data context. Can be a JavaScript array, DataManager instance, or remote data source.
// Local array
dataSource = [
{ id: 1, name: 'Alice', dept: 'Engineering' },
{ id: 2, name: 'Bob', dept: 'Sales' }
];
// Remote DataManager
dataSource = new DataManager({
url: 'url',
adaptor: new ODataV4Adaptor()
});See data-binding.md for detailed examples.
---
displayMode
Type: DisplayMode Default: 'Horizontal'
Specifies the layout mode: 'Horizontal' or 'Vertical'.
<!-- Horizontal layout (default) -->
<ejs-querybuilder displayMode="Horizontal"></ejs-querybuilder>
<!-- Vertical layout -->
<ejs-querybuilder displayMode="Vertical"></ejs-querybuilder>---
enableNotCondition
Type: boolean Default: false
Enables or disables the NOT condition for groups.
<ejs-querybuilder [enableNotCondition]="true"></ejs-querybuilder>---
enablePersistence
Type: boolean Default: false
Persists Query Builder state (rules) to browser localStorage across page reloads.
<ejs-querybuilder [enablePersistence]="true"></ejs-querybuilder>---
enableRtl
Type: boolean Default: false
Renders the component in right-to-left direction for RTL languages.
<ejs-querybuilder [enableRtl]="true"></ejs-querybuilder>See accessibility-and-localization.md for RTL details.
---
enableSeparateConnector
Type: boolean Default: false
Shows separate AND/OR connectors between individual rules instead of per-group.
<ejs-querybuilder [enableSeparateConnector]="true"></ejs-querybuilder>See filtering-and-rules.md for examples.
---
fieldMode
Type: FieldMode Default: 'Default'
Specifies field dropdown mode: 'Default' (DropDownList) or 'DropDownTree' for hierarchical fields.
<ejs-querybuilder fieldMode="DropDownTree"></ejs-querybuilder>---
fieldModel
Type: DropDownListModel | DropDownTreeModel Default: null
Configures field dropdown properties (placeholder, filtering, etc.).
fieldModel = {
placeholder: 'Select a field',
allowFiltering: true,
dataSource: []
};---
headerTemplate
Type: any Default: null
Custom template for the Query Builder header area.
<ejs-querybuilder [headerTemplate]="headerTemplate"></ejs-querybuilder>See templates.md for template examples.
---
height
Type: string Default: 'auto'
Sets the height of the Query Builder container. Can be pixel values or percentages.
<ejs-querybuilder height="500px"></ejs-querybuilder>
<ejs-querybuilder height="100%"></ejs-querybuilder>---
immediateModeDelay
Type: number Default: 0
Delay (in milliseconds) before triggering the ruleChange event after modifications.
<ejs-querybuilder [immediateModeDelay]="500"></ejs-querybuilder>---
locale
Type: string Default: ''
Overrides the global locale for this component instance. Example values: 'en-US', 'de', 'fr', 'ar', etc.
<ejs-querybuilder locale="de"></ejs-querybuilder>See accessibility-and-localization.md for all supported locales.
---
matchCase
Type: boolean Default: false
If true, string comparisons are case-sensitive. If false, comparisons are case-insensitive.
<ejs-querybuilder [matchCase]="true"></ejs-querybuilder>---
maxGroupCount
Type: number Default: 5
Limits the maximum nesting depth of groups. Prevents over-complex query hierarchies.
<ejs-querybuilder [maxGroupCount]="3"></ejs-querybuilder>---
operatorModel
Type: DropDownListModel Default: null
Configures operator dropdown properties (placeholder, filtering, etc.).
operatorModel = {
placeholder: 'Select operator',
allowFiltering: true
};---
readonly
Type: boolean Default: false
When true, disables all user interactions on the component (read-only mode).
<ejs-querybuilder [readonly]="true"></ejs-querybuilder>---
rule
Type: RuleModel Default: {}
Defines the initial rules to be rendered in the Query Builder.
rule = {
condition: 'and',
rules: [
{ field: 'EmployeeID', operator: 'equal', value: 1 },
{ field: 'FirstName', operator: 'contains', value: 'John' }
]
};See import-export.md for RuleModel structure.
---
separator
Type: string Default: ''
Separator string for hierarchical column names.
<ejs-querybuilder separator="_"></ejs-querybuilder>---
showButtons
Type: ShowButtonsModel Default: { ruleDelete: true, groupInsert: true, groupDelete: true }
Controls visibility of action buttons in the Query Builder UI.
showButtons = {
ruleDelete: true,
groupInsert: true,
groupDelete: true,
ruleInsert: true,
cloneRule: true,
cloneGroup: true,
lockRule: true,
lockGroup: true
};---
sortDirection
Type: SortDirection Default: 'Default'
Specifies sorting order of field dropdown: 'Default', 'Ascending', or 'Descending'.
<ejs-querybuilder sortDirection="Ascending"></ejs-querybuilder>See accessibility-and-localization.md for details.
---
summaryView
Type: boolean Default: false
Displays a human-readable summary of the current query below the rules.
<ejs-querybuilder [summaryView]="true"></ejs-querybuilder>---
valueModel
Type: ValueModel Default: null
Configures the value input properties based on field type.
valueModel = {
dataSource: [],
allowFiltering: true,
fields: { text: 'name', value: 'id' }
};---
width
Type: string Default: 'auto'
Sets the width of the Query Builder container. Can be pixel values or percentages.
<ejs-querybuilder width="800px"></ejs-querybuilder>---
Methods
addGroups
Adds one or more groups with rules to the Query Builder.
Signature:
addGroups(groups: RuleModel[], groupID?: string): voidParameters:
| Parameter | Type | Description |
|---|---|---|
groups | RuleModel[] | Array of group objects to add |
groupID | string | (Optional) Parent group ID. If omitted, adds to root |
Example:
const newGroups = [
{
condition: 'or',
rules: [
{ field: 'City', operator: 'equal', value: 'London' }
]
}
];
this.qb.addGroups(newGroups, 'querybuilder_group0');---
addRules
Adds one or more rules to the Query Builder.
Signature:
addRules(rule: RuleModel[], groupID?: string): voidParameters:
| Parameter | Type | Description |
|---|---|---|
rule | RuleModel[] | Array of rule objects to add |
groupID | string | (Optional) Group ID. If omitted, adds to root group |
Example:
const newRules = [
{ field: 'Salary', operator: 'greaterThan', value: 50000 },
{ field: 'Title', operator: 'contains', value: 'Manager' }
];
this.qb.addRules(newRules, 'querybuilder_group0');---
cloneGroup
Clones an existing group to a specified parent group.
Signature:
cloneGroup(groupID: string, parentGroupID?: string, index?: number): voidParameters:
| Parameter | Type | Description |
|---|---|---|
groupID | string | ID of the group to clone |
parentGroupID | string | (Optional) Parent group ID for the clone |
index | number | (Optional) Position index in parent |
Example:
this.qb.cloneGroup('querybuilder_group1', 'querybuilder_group0', 0);See clone-group-rule.md for detailed documentation.
---
cloneRule
Clones an existing rule to a specified group.
Signature:
cloneRule(ruleID: string, groupID?: string, index?: number): voidParameters:
| Parameter | Type | Description |
|---|---|---|
ruleID | string | ID of the rule to clone |
groupID | string | (Optional) Target group ID |
index | number | (Optional) Position index in group |
Example:
this.qb.cloneRule('querybuilder_rule0', 'querybuilder_group0');---
deleteGroup
Deletes a group from the Query Builder.
Signature:
deleteGroup(target: Element | string): voidParameters:
| Parameter | Type | Description |
|---|---|---|
target | `Element \ | string` |
Example:
this.qb.deleteGroup('querybuilder_group1');---
deleteGroups
Deletes multiple groups by their IDs.
Signature:
deleteGroups(groupIdColl: string[]): voidParameters:
| Parameter | Type | Description |
|---|---|---|
groupIdColl | string[] | Array of group IDs to delete |
Example:
this.qb.deleteGroups(['querybuilder_group1', 'querybuilder_group2']);---
deleteRules
Deletes multiple rules by their IDs.
Signature:
deleteRules(ruleIdColl: string[]): voidParameters:
| Parameter | Type | Description |
|---|---|---|
ruleIdColl | string[] | Array of rule IDs to delete |
Example:
this.qb.deleteRules(['querybuilder_rule0', 'querybuilder_rule1']);---
destroy
Removes the component from the DOM and detaches all event handlers.
Signature:
destroy(): voidExample:
ngOnDestroy() {
this.qb.destroy();
}---
getDataManagerQuery
Generates a DataManager query from the current rules (used with getPredicate).
Signature:
getDataManagerQuery(rule?: RuleModel): QueryParameters:
| Parameter | Type | Description |
|---|---|---|
rule | RuleModel | (Optional) Specific rule; uses current rules if omitted |
Returns: Query - DataManager Query object
Example:
const query = this.qb.getDataManagerQuery();
const filteredData = this.dataManager.executeQuery(query).then(e => e.result);---
getFilteredRecords
Returns filtered records based on current rules applied to the dataSource.
Signature:
getFilteredRecords(): Promise<object>Returns: Promise<object> - Promise resolving to filtered records
Example:
this.qb.getFilteredRecords().then(records => {
console.log('Filtered records:', records);
});---
getGroup
Retrieves the group object for a specified element or ID.
Signature:
getGroup(target: Element | string): RuleModelParameters:
| Parameter | Type | Description |
|---|---|---|
target | `Element \ | string` |
Returns: RuleModel - Group rule object
Example:
const group = this.qb.getGroup('querybuilder_group0');
console.log(group.condition); // 'and' or 'or'---
getMongoQuery
Exports current rules as a MongoDB query string.
Signature:
getMongoQuery(rule?: RuleModel): stringParameters:
| Parameter | Type | Description |
|---|---|---|
rule | RuleModel | (Optional) Specific rule; uses current rules if omitted |
Returns: string - MongoDB query string
Example:
const mongoQuery = this.qb.getMongoQuery();
console.log(mongoQuery);
// Output: { "$and": [{ "EmployeeID": { "$eq": 1 } }, { "FirstName": { "$regex": "John" } }] }See import-export.md for MongoDB export details.
---
getOperators
Returns the list of operators available for a specific field or column.
Signature:
getOperators(): { [key: string]: Object }[]Returns: { [key: string]: Object }[] - Array of operator objects
Example:
const operators = this.qb.getOperators();
console.log(operators);---
getParameterizedNamedSql
Generates a parameterized named SQL query from current rules.
Signature:
getParameterizedNamedSql(rule?: RuleModel): ParameterizedNamedSqlParameters:
| Parameter | Type | Description |
|---|---|---|
rule | RuleModel | (Optional) Specific rule; uses current rules if omitted |
Returns: ParameterizedNamedSql - Object with sql and params properties
Example:
const paramQuery = this.qb.getParameterizedNamedSql();
// { sql: "SELECT * FROM Employees WHERE @EmployeeID = :EmployeeID", params: { EmployeeID: 1 } }---
getParameterizedSql
Generates a parameterized SQL query with ? placeholders.
Signature:
getParameterizedSql(rule?: RuleModel): ParameterizedSqlParameters:
| Parameter | Type | Description |
|---|---|---|
rule | RuleModel | (Optional) Specific rule; uses current rules if omitted |
Returns: ParameterizedSql - Object with sql and params properties
Example:
const paramQuery = this.qb.getParameterizedSql();
// { sql: "SELECT * FROM Employees WHERE EmployeeID = ? AND FirstName LIKE ?", params: [1, "John"] }See import-export.md for parameterized SQL details.
---
getPredicate
Generates a Syncfusion Predicate object for use with DataManager filtering.
Signature:
getPredicate(rule: RuleModel): PredicateParameters:
| Parameter | Type | Description |
|---|---|---|
rule | RuleModel | Rule to convert to predicate |
Returns: Predicate - Syncfusion Predicate object
Example:
const predicate = this.qb.getPredicate(this.qb.getRules());
this.dataManager.executeQuery(new Query().where(predicate)).then(e => {
console.log(e.result);
});---
getRule
Retrieves the rule object for a specified element or ID.
Signature:
getRule(elem: string | HTMLElement): RuleModelParameters:
| Parameter | Type | Description |
|---|---|---|
elem | `string \ | HTMLElement` |
Returns: RuleModel - Rule object
Example:
const rule = this.qb.getRule('querybuilder_rule0');
console.log(rule.field, rule.operator, rule.value);---
getRules
Returns the complete rules collection as a RuleModel object.
Signature:
getRules(): RuleModelReturns: RuleModel - Current rules in the Query Builder
Example:
const rules = this.qb.getRules();
console.log(JSON.stringify(rules, null, 2));---
getRulesFromSql
Parses a SQL query string and converts it to Query Builder rules.
Signature:
getRulesFromSql(sqlString: string, sqlLocale?: boolean): RuleModelParameters:
| Parameter | Type | Description |
|---|---|---|
sqlString | string | SQL WHERE clause (e.g., "EmployeeID = 1 AND FirstName LIKE 'John'") |
sqlLocale | boolean | (Optional) Apply localization to SQL parsing |
Returns: RuleModel - Parsed rules object
Example:
const sql = "EmployeeID = 1 AND FirstName LIKE '%John%'";
const rules = this.qb.getRulesFromSql(sql);See import-export.md for SQL import details.
---
getSqlFromRules
Exports current rules as a SQL WHERE clause string.
Signature:
getSqlFromRules(rule?: RuleModel, allowEscape?: boolean, sqlLocale?: boolean): stringParameters:
| Parameter | Type | Description |
|---|---|---|
rule | RuleModel | (Optional) Specific rule; uses current rules if omitted |
allowEscape | boolean | (Optional) Exclude escape characters if true |
sqlLocale | boolean | (Optional) Apply localization to SQL output |
Returns: string - SQL WHERE clause
Example:
const sql = this.qb.getSqlFromRules();
console.log(sql);
// Output: "EmployeeID = 1 AND FirstName LIKE '%John%'"---
getValidRules
Returns only the valid rules from the collection (filters out invalid ones).
Signature:
getValidRules(currentRule?: RuleModel): RuleModelParameters:
| Parameter | Type | Description |
|---|---|---|
currentRule | RuleModel | (Optional) Specific rule to validate |
Returns: RuleModel - Valid rules only
Example:
const validRules = this.qb.getValidRules();---
getValues
Retrieves distinct values available for a specific field (for dropdowns).
Signature:
getValues(field: string): object[]Parameters:
| Parameter | Type | Description |
|---|---|---|
field | string | Field name to get values for |
Returns: object[] - Array of distinct values
Example:
const countries = this.qb.getValues('Country');
console.log(countries); // ['USA', 'UK', 'Canada', ...]---
lockGroup
Makes a group and all its contained rules read-only.
Signature:
lockGroup(groupID: string): voidParameters:
| Parameter | Type | Description |
|---|---|---|
groupID | string | ID of the group to lock |
Example:
this.qb.lockGroup('querybuilder_group0');See lock-group-rule.md for locking examples.
---
lockRule
Makes a rule read-only (prevents editing).
Signature:
lockRule(ruleID: string): voidParameters:
| Parameter | Type | Description |
|---|---|---|
ruleID | string | ID of the rule to lock |
Example:
this.qb.lockRule('querybuilder_rule0');---
notifyChange
Notifies the component of external value changes to update the UI.
Signature:
notifyChange(value: string | number | boolean | Date | string[] | number[] | Date[], element: Element, type?: string): voidParameters:
| Parameter | Type | Description |
|---|---|---|
value | Value types | New value to update |
element | Element | DOM element to update |
type | string | (Optional) Update type |
Example:
this.qb.notifyChange('NewValue', element);---
reset
Clears all rules from the Query Builder (resets to empty state).
Signature:
reset(): voidExample:
this.qb.reset();---
setMongoQuery
Imports a MongoDB query string and populates the Query Builder.
Signature:
setMongoQuery(mongoQuery: string, mongoLocale?: boolean): voidParameters:
| Parameter | Type | Description |
|---|---|---|
mongoQuery | string | MongoDB query string |
mongoLocale | boolean | (Optional) Apply localization |
Example:
const mongoQuery = '{ "$and": [{ "EmployeeID": { "$eq": 1 } }] }';
this.qb.setMongoQuery(mongoQuery);See import-export.md for MongoDB import details.
---
setParameterizedNamedSql
Imports a parameterized named SQL query into the Query Builder.
Signature:
setParameterizedNamedSql(sqlQuery: ParameterizedNamedSql): voidParameters:
| Parameter | Type | Description |
|---|---|---|
sqlQuery | ParameterizedNamedSql | Object with sql and named parameters |
Example:
const paramQuery = {
sql: "SELECT * FROM Employees WHERE EmployeeID = @EmployeeID",
params: { EmployeeID: 1 }
};
this.qb.setParameterizedNamedSql(paramQuery);---
setParameterizedSql
Imports a parameterized SQL query (with ? placeholders) into the Query Builder.
Signature:
setParameterizedSql(sqlQuery: ParameterizedSql): voidParameters:
| Parameter | Type | Description |
|---|---|---|
sqlQuery | ParameterizedSql | Object with sql and params array |
Example:
const paramQuery = {
sql: "SELECT * FROM Employees WHERE EmployeeID = ? AND FirstName LIKE ?",
params: [1, "John"]
};
this.qb.setParameterizedSql(paramQuery);---
setRules
Sets or replaces the complete rules in the Query Builder.
Signature:
setRules(rule: RuleModel): voidParameters:
| Parameter | Type | Description |
|---|---|---|
rule | RuleModel | Rule object to set |
Example:
const newRules = {
condition: 'and',
rules: [
{ field: 'EmployeeID', operator: 'equal', value: 1 },
{ field: 'Salary', operator: 'greaterThan', value: 50000 }
]
};
this.qb.setRules(newRules);---
setRulesFromSql
Imports inline SQL and converts it to Query Builder rules.
Signature:
setRulesFromSql(sqlString: string, sqlLocale?: boolean): voidParameters:
| Parameter | Type | Description |
|---|---|---|
sqlString | string | SQL WHERE clause to import |
sqlLocale | boolean | (Optional) Apply localization |
Example:
const sql = "EmployeeID = 1 AND FirstName LIKE '%John%'";
this.qb.setRulesFromSql(sql);---
validateFields
Triggers validation on all fields and displays error messages for invalid entries.
Signature:
validateFields(): booleanReturns: boolean - true if all fields are valid, false otherwise
Example:
if (this.qb.validateFields()) {
console.log('All validations passed');
const rules = this.qb.getRules();
// Process valid rules
} else {
console.log('Validation failed');
}---
Events
actionBegin
Triggers when field, operator, or value selection changes.
Type: EmitType<ActionEventArgs>
Example:
<ejs-querybuilder (actionBegin)="onActionBegin($event)"></ejs-querybuilder>onActionBegin(args: ActionEventArgs) {
console.log('Action:', args.action);
console.log('Current rules:', args.value);
}---
beforeChange
Triggers before any change to condition, field, operator, or value.
Type: EmitType<ChangeEventArgs>
Example:
<ejs-querybuilder (beforeChange)="onBeforeChange($event)"></ejs-querybuilder>onBeforeChange(args: ChangeEventArgs) {
console.log('Before change - field:', args.field);
// Can prevent change by setting args.cancel = true
}---
change
Triggers after any change to condition, field, operator, or value.
Type: EmitType<ChangeEventArgs>
Example:
<ejs-querybuilder (change)="onChange($event)"></ejs-querybuilder>onChange(args: ChangeEventArgs) {
console.log('Changed - field:', args.field);
console.log('New value:', args.value);
}---
created
Triggers after the Query Builder component is successfully created and rendered.
Type: EmitType<Event>
Example:
<ejs-querybuilder (created)="onCreated($event)"></ejs-querybuilder>onCreated(args: Event) {
console.log('Query Builder created');
// Component ready for interaction
}---
dataBound
Triggers when data is bound to the Query Builder (from dataSource).
Type: EmitType<Object>
Example:
<ejs-querybuilder (dataBound)="onDataBound($event)"></ejs-querybuilder>onDataBound(args: any) {
console.log('Data bound to Query Builder');
}---
destroyed
Triggers when the Query Builder component is destroyed.
Type: EmitType<Object>
Example:
<ejs-querybuilder (destroyed)="onDestroyed($event)"></ejs-querybuilder>onDestroyed(args: any) {
console.log('Query Builder destroyed');
}---
ruleChange
Triggers when the rules collection changes (condition, field, operator, or value).
Type: EmitType<RuleChangeEventArgs>
Example:
<ejs-querybuilder (ruleChange)="onRuleChange($event)"></ejs-querybuilder>onRuleChange(args: RuleChangeEventArgs) {
console.log('Rules changed');
console.log('Updated rules:', args.value);
// Export to SQL/MongoDB if needed
const sql = this.qb.getSqlFromRules();
console.log('Generated SQL:', sql);
}---
Interfaces & Types
RuleModel
Represents a rule or group of rules.
interface RuleModel {
condition?: string; // 'and' | 'or'
not?: boolean; // Enable NOT condition (requires enableNotCondition)
rules?: RuleModel[]; // Sub-rules for groups
field?: string; // Field name for individual rules
type?: string; // Field data type
operator?: string; // Comparison operator
value?: any; // Comparison value
label?: string; // Field display label
level?: number; // Nesting level
}ColumnsModel
Represents a column/field definition.
interface ColumnsModel {
field: string; // Unique field identifier
label?: string; // Display label
type?: string; // 'string' | 'number' | 'date' | 'boolean' | 'dropdown'
dataSource?: any[]; // Values for dropdown type
operators?: OperatorModel[]; // Custom operators for this field
values?: any[]; // Predefined values
template?: string; // Custom value template
validationRules?: any; // Validation configuration
format?: string; // Format string (for date/number)
step?: number; // Step value (for number type)
min?: number | Date; // Minimum value
max?: number | Date; // Maximum value
value?: any; // Default value
optionsText?: string; // Key for dropdown display text
optionsValue?: string; // Key for dropdown value
}ShowButtonsModel
Controls button visibility.
interface ShowButtonsModel {
ruleDelete?: boolean; // Show delete rule button
groupInsert?: boolean; // Show add group button
groupDelete?: boolean; // Show delete group button
ruleInsert?: boolean; // Show add rule button
cloneRule?: boolean; // Show clone rule button
cloneGroup?: boolean; // Show clone group button
lockRule?: boolean; // Show lock rule button
lockGroup?: boolean; // Show lock group button
}ParameterizedSql
Represents parameterized SQL with ? placeholders.
interface ParameterizedSql {
sql: string; // SQL query with ? placeholders
params: any[]; // Parameter values array
}ParameterizedNamedSql
Represents parameterized SQL with named parameters.
interface ParameterizedNamedSql {
sql: string; // SQL query with named parameters (@ParamName or :ParamName)
params: { [key: string]: any }; // Named parameter values
}DisplayMode
Type for display layout mode.
type DisplayMode = 'Horizontal' | 'Vertical';FieldMode
Type for field dropdown mode.
type FieldMode = 'Default' | 'DropDownTree';SortDirection
Type for field sorting.
type SortDirection = 'Default' | 'Ascending' | 'Descending';---
Related Documentation
- Getting Started - Setup and basic usage
- Columns Configuration - Field and operator setup
- Data Binding - Local and remote data sources
- Filtering & Rules - Rule management
- Import/Export - SQL, MongoDB, JSON export/import
- Templates - Custom UI templates
- Style & Appearance - Theming and CSS
- Accessibility & Localization - i18n and WCAG
---
Official References
- Syncfusion Angular QueryBuilder API: https://ej2.syncfusion.com/angular/documentation/api/query-builder/
- GitHub Repository: https://github.com/syncfusion/ej2-angular-ui-components
- NpmJS Package: https://www.npmjs.com/package/@syncfusion/ej2-angular-querybuilder
Columns — Syncfusion Angular Query Builder
Column definitions control how the Query Builder renders fields, which operators are available, how values are formatted, and how validation is applied.
---
Column Definition Basics
Each <e-column> maps to one queryable field. The field property is required — it must match the key name in your dataSource.
<ejs-querybuilder [dataSource]="data">
<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="IsActive" label="Active" type="boolean"></e-column>
<e-column field="HireDate" label="Hire Date" type="date" format="dd/MM/yyyy"></e-column>
<e-column field="TitleOfCourtesy" label="Title of Courtesy" type="string" [values]="['Mr.','Mrs.']"></e-column>
</e-columns>
</ejs-querybuilder>Column types:
| Type | Rendered input | Default operators |
|---|---|---|
string | TextBox (or dropdown if values set) | startswith, endswith, contains, equal, notequal, in, notin |
number | NumericTextBox | equal, notequal, greaterthan, greaterthanorequal, lessthan, lessthanorequal, between, notbetween |
date | DatePicker | equal, notequal, greaterthan, lessthan, between, notbetween |
boolean | RadioButton (True/False) | equal, notequal |
If field is not specified, the column values render empty.---
Auto-Generation from DataSource
When <e-columns> is empty or omitted, columns are automatically generated from the bound dataSource:
@Component({
template: `<ejs-querybuilder [dataSource]="data"></ejs-querybuilder>`
})
export class App {
public data = [
{ EmployeeID: 1, FirstName: 'Nancy', Country: 'USA' },
{ EmployeeID: 2, FirstName: 'Andrew', Country: 'UK' }
];
}Auto-generated column types are inferred from the first record in the dataSource. Always provide a non-null first record for accurate type detection.
---
Labels
By default, the Query Builder displays the field value as the column label. Override it with label:
<e-column field="emp_id" label="Employee ID" type="number"></e-column>---
Operators
Define the available operators for a column using the operators property. If omitted, default operators for the column's type are used.
public customOperators = [
{ value: 'equal', text: 'Equal' },
{ value: 'notequal', text: 'Not Equal' },
{ value: 'contains', text: 'Contains' },
{ value: 'startswith', text: 'Starts With' }
];<e-column field="FirstName" label="First Name" type="string" [operators]="customOperators"></e-column>All Available Operators
| Operator | Description | Supported Types |
|---|---|---|
startswith | Value begins with | String |
endswith | Value ends with | String |
contains | Value contains | String |
doesnotstartwith | Does not start with | String |
doesnotendwith | Does not end with | String |
doesnotcontain | Does not contain | String |
equal | Exactly equal | String, Number, Date, Boolean |
notequal | Not equal | String, Number, Date, Boolean |
greaterthan | Greater than | Date, Number |
greaterthanorequal | Greater than or equal | Date, Number |
lessthan | Less than | Date, Number |
lessthanorequal | Less than or equal | Date, Number |
between | Between two values | Date, Number |
notbetween | Not between two values | Date, Number |
in | One of a set of values | String, Number |
notin | Not in a set of values | String, Number |
isempty | Value is empty string | String |
isnotempty | Value is not empty string | String |
isnull | Value is null | String, Number |
isnotnull | Value is not null | String, Number |
---
Step (Number Fields)
Set the increment step for numeric inputs to make value entry easier:
<e-column field="Age" label="Age" type="number" [step]="5"></e-column>---
Format (Date and Number Fields)
Apply display format to date or number columns:
<!-- Date: dd/MM/yyyy -->
<e-column field="HireDate" label="Hire Date" type="date" format="dd/MM/yyyy"></e-column>
<!-- Number: 2 decimal places -->
<e-column field="Salary" label="Salary" type="number" format="n2"></e-column>// Format via columnsModel in component
public columns: ColumnsModel[] = [
{ field: 'HireDate', label: 'Hire Date', type: 'date', format: 'dd/MM/yyyy' },
{ field: 'Salary', label: 'Salary', type: 'number', format: 'n2' }
];---
Validation
Enable validation to show errors when rules are incomplete or out of range. Set allowValidation on the Query Builder and configure validation per column.
<ejs-querybuilder [allowValidation]="true">
<e-columns>
<e-column field="FirstName" label="First Name" type="string"
[validation]="{ isRequired: true }">
</e-column>
<e-column field="Age" label="Age" type="number"
[validation]="{ isRequired: true, min: 18, max: 65 }">
</e-column>
</e-columns>
</ejs-querybuilder>Trigger validation programmatically:
@ViewChild('querybuilder') qb!: QueryBuilderComponent;
validate(): boolean {
return this.qb.validateFields();
}Validation options:
| Option | Type | Description |
|---|---|---|
isRequired | boolean | Field/operator/value must be filled |
min | number | Minimum allowed value (number fields) |
max | number | Maximum allowed value (number fields) |
Set isRequired for both Operator and Value fields for complete validation coverage.---
Predefined Values (Dropdown for String Columns)
Use values on a string column to render a dropdown instead of a free-text input:
<e-column field="Status" label="Status" type="string"
[values]="['Active', 'Inactive', 'Pending']">
</e-column>---
Troubleshooting
| Issue | Solution |
|---|---|
| Column shows empty values | Verify field matches the exact key in dataSource |
| Wrong operators shown | Set [operators] explicitly on the column |
| Date picker not rendering | Ensure type="date" and CSS imports include ej2-calendars |
| Validation not triggering | Set [allowValidation]="true" on <ejs-querybuilder> and call validateFields() |
| Auto-generated types wrong | Ensure first record in dataSource has non-null values for all fields |
Data Binding — Syncfusion Angular Query Builder
The Query Builder uses DataManager under the hood to support both local and remote data sources. The dataSource drives auto-column generation and powers the getPredicate() method for in-memory filtering.
---
Local Data Binding
Bind a plain JavaScript object array directly to dataSource:
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder';
import { Component } from '@angular/core';
@Component({
imports: [QueryBuilderModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-querybuilder width="70%" [dataSource]="data" [rule]="importRules">
<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="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
`
})
export class App {
public data = [
{ EmployeeID: 1, FirstName: 'Nancy', Country: 'USA' },
{ EmployeeID: 2, FirstName: 'Andrew', Country: 'UK' },
{ EmployeeID: 3, FirstName: 'Janet', Country: 'USA' }
];
public importRules = {
condition: 'and',
rules: [{ field: 'Country', operator: 'equal', value: 'USA' }]
};
}By default, DataManager uses JsonAdaptor for local data binding.Using DataManager for Local Data
import { DataManager } from '@syncfusion/ej2-data';
export class App {
public data: DataManager = new DataManager(employeeData);
}---
Remote Data Binding
Bind remote data by assigning a DataManager instance with a service URL.
OData Service (v3)
import { DataManager, ODataAdaptor } from '@syncfusion/ej2-data';
import { Component } from '@angular/core';
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder';
@Component({
imports: [QueryBuilderModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-querybuilder width="70%" [dataSource]="data" [rule]="importRules">
<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="Title" label="Title" type="string"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
`
})
export class App {
public data: DataManager = new DataManager({
url: 'url',
adaptor: new ODataAdaptor()
});
public importRules = {
condition: 'and',
rules: [{ field: 'EmployeeID', operator: 'equal', value: 1 }]
};
}By default, DataManager uses ODataAdaptor for remote data binding.OData v4 Service
import { DataManager, ODataV4Adaptor } from '@syncfusion/ej2-data';
public data: DataManager = new DataManager({
url: 'url',
adaptor: new ODataV4Adaptor()
});WebAPI Adaptor
Use WebApiAdaptor for custom REST APIs built with OData conventions:
import { DataManager, WebApiAdaptor } from '@syncfusion/ej2-data';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<ejs-querybuilder width="70%" [dataSource]="data" [rule]="importRules">
<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="Title" label="Title" type="string"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
<e-column field="City" label="City" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
`
})
export class AppComponent implements OnInit {
public data!: DataManager;
public importRules: any;
ngOnInit(): void {
this.data = new DataManager({
url: 'url',
adaptor: new WebApiAdaptor()
});
this.importRules = {
condition: 'and',
rules: [
{ label: 'Employee ID', field: 'EmployeeID', type: 'number', operator: 'equal', value: 1 },
{ label: 'Title', field: 'Title', type: 'string', operator: 'equal', value: 'Sales Manager' }
]
};
}
}---
Using DataManager with getPredicate()
After the user builds a query, use getPredicate() to filter local data in-memory:
import { QueryBuilderComponent } from '@syncfusion/ej2-angular-querybuilder';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { ViewChild, Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<ejs-querybuilder #qb width="70%" [dataSource]="employeeData">
<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="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
<button (click)="filterData()">Filter</button>
<p>Matched: {{ filteredCount }}</p>
`
})
export class App {
@ViewChild('qb') qb!: QueryBuilderComponent;
public employeeData = [
{ EmployeeID: 1, FirstName: 'Nancy', Country: 'USA' },
{ EmployeeID: 2, FirstName: 'Andrew', Country: 'UK' },
{ EmployeeID: 3, FirstName: 'Janet', Country: 'USA' }
];
public filteredCount = 0;
filterData(): void {
const rules = this.qb.getRules();
const predicate = this.qb.getPredicate(rules);
const dm = new DataManager(this.employeeData);
const result = dm.executeLocal(new Query().where(predicate));
this.filteredCount = result.length;
}
}---
Complex Data Binding (Nested Columns)
Complex data binding lets you query nested object properties using a separator (default: .).
// Data with nested objects
public complexData = [
{
Employee: { ID: 1, Name: 'Nancy' },
Address: { City: 'Seattle', Country: 'USA' }
}
];<ejs-querybuilder [dataSource]="complexData" separator=".">
<e-columns>
<!-- Top-level wrapper column -->
<e-column field="Employee" label="Employee">
<e-columns>
<e-column field="ID" label="ID" type="number"></e-column>
<e-column field="Name" label="Name" type="string"></e-column>
</e-columns>
</e-column>
<e-column field="Address" label="Address">
<e-columns>
<e-column field="City" label="City" type="string"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</e-column>
</e-columns>
</ejs-querybuilder>The resulting rule field will be "Employee.ID", "Address.City", etc., matching the nested property path.
---
Troubleshooting
| Issue | Solution |
|---|---|
| Remote data not loading | Verify URL is reachable and adaptor type matches service format |
| OData v4 errors | Use ODataV4Adaptor instead of ODataAdaptor |
getPredicate returns no results | Ensure rule values match the exact casing/type in your data |
| Nested fields not available | Confirm separator matches the path separator in your data keys |
| Auto-column types wrong on remote data | Specify columns explicitly instead of relying on auto-generation |
Filtering & Rules Management — Syncfusion Angular Query Builder
Manage query conditions and groups programmatically or through UI interactions. Control which action buttons are visible and enable advanced features like drag-and-drop, cloning, locking, and separate connectors.
---
Table of Contents
- Creating and Deleting Rules Programmatically
- Creating and Deleting Groups Programmatically
- Controlling Button Visibility
- Clone Rule and Group
- Lock Rule and Group
- Separate Connector
- Restrict Group Nesting Depth
- Drag and Drop Reordering
---
Creating and Deleting Rules Programmatically
Use addRules() and deleteRules() to manage individual conditions at runtime.
import { QueryBuilderComponent } from '@syncfusion/ej2-angular-querybuilder';
import { ViewChild, Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<ejs-querybuilder #qb width="70%" [rule]="importRules">
<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="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
<button (click)="addRule()">Add Rule</button>
<button (click)="deleteRule()">Delete Rule</button>
`
})
export class App {
@ViewChild('qb') qb!: QueryBuilderComponent;
public importRules = {
condition: 'and',
rules: [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number', operator: 'equal', value: 1 }
]
};
addRule(): void {
// Add a rule to root group (always 'querybuilder_group0')
this.qb.addRules(
[{ field: 'Country', label: 'Country', type: 'string', operator: 'equal', value: 'USA' }],
'querybuilder_group0'
);
}
deleteRule(): void {
// Get rule IDs from current rules and delete the second one
const rules = this.qb.getRules();
if (rules.rules && rules.rules.length > 1) {
// Rule IDs follow pattern: querybuilder_group0_rule0, querybuilder_group0_rule1, etc.
this.qb.deleteRules(['querybuilder_group0_rule1']);
}
}
}The root group ID is always 'querybuilder_group0'. Sub-groups increment numerically.---
Creating and Deleting Groups Programmatically
Use addGroups() and deleteGroups() to manage nested groups.
addGroup(): void {
this.qb.addGroups(
[{
condition: 'or',
rules: [
{ field: 'FirstName', label: 'First Name', type: 'string', operator: 'startswith', value: 'J' }
]
}],
'querybuilder_group0' // parent group ID
);
}
deleteGroup(): void {
this.qb.deleteGroups(['querybuilder_group1']);
}---
Controlling Button Visibility
Use showButtons to control which action buttons appear in the Query Builder UI:
public showButtons = {
ruleDelete: true, // Delete condition button
groupInsert: true, // Add Group/Condition button
groupDelete: true, // Delete Group button
ruleInsert: true, // Add Condition button (within group)
cloneRule: true, // Clone individual rule
cloneGroup: true, // Clone entire group
lockRule: true, // Lock individual rule
lockGroup: true // Lock entire group
};<ejs-querybuilder [showButtons]="showButtons">
</ejs-querybuilder>Hide all buttons except delete (read-only display):
public showButtons = {
ruleDelete: false, groupInsert: false, groupDelete: false,
ruleInsert: false, cloneRule: false, cloneGroup: false,
lockRule: false, lockGroup: false
};---
Clone Rule and Group
Cloning creates an exact duplicate adjacent to the original — useful for building similar conditions quickly. Enable clone buttons via showButtons, or trigger cloning programmatically.
<ejs-querybuilder [showButtons]="showButtons" [rule]="importRules">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>public showButtons = { cloneRule: true, cloneGroup: true, ruleDelete: true, groupDelete: true, groupInsert: true };
// Programmatic cloning
cloneFirstRule(): void {
this.qb.cloneRule('querybuilder_group0_rule0');
}
cloneRootGroup(): void {
// Clone a sub-group (cannot clone the root group)
this.qb.cloneGroup('querybuilder_group1');
}Cloning the root group (querybuilder_group0) is not supported. Only sub-groups can be cloned.---
Lock Rule and Group
Locking makes a rule or group read-only — the user cannot modify fields, operators, or values. Useful for enforcing mandatory query conditions.
public showButtons = { lockRule: true, lockGroup: true, ruleDelete: true, groupInsert: true, groupDelete: true };
// Lock a specific rule programmatically
lockRule(): void {
this.qb.lockRule('querybuilder_group0_rule0');
}
// Lock an entire group (all nested rules become read-only)
lockGroup(): void {
this.qb.lockGroup('querybuilder_group1');
}When a group is locked, all rules and nested groups within it become read-only simultaneously.
---
Separate Connector
By default, all rules in a group share the same AND/OR connector (set at group level). Enabling separate connectors allows each rule to have its own AND/OR choice, enabling mixed logic like:
(A AND B) OR C AND (D OR E)
<ejs-querybuilder [enableSeparateConnector]="true" [rule]="importRules">
<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="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>When enabled, the group-level AND/OR selector is removed and each rule gets its own connector dropdown.
---
Restrict Group Nesting Depth
Use maxGroupCount to limit how many nested groups users can create. Default is 5.
<ejs-querybuilder [maxGroupCount]="2">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>Particularly useful for mobile interfaces or when backend query parsers have nesting limits.
---
Drag and Drop Reordering
Enable drag-and-drop to let users reorder rules and groups within the Query Builder by dragging them to new positions.
<ejs-querybuilder [allowDragAndDrop]="true" [rule]="importRules" (dragStart)="onDragStart($event)" (drop)="onDrop($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="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>onDragStart(args: any): void {
console.log('Drag started:', args.draggedNodeData);
}
onDrop(args: any): void {
console.log('Dropped at:', args.droppedNodeData);
}Drag and drop events:
| Event | When it fires |
|---|---|
(dragStart) | When dragging begins on a rule or group |
(drag) | Continuously while dragging |
(drop) | When the dragged item is released at a new position |
---
NOT Condition on Groups
Enable a NOT toggle on each group header to negate the entire group's condition:
<ejs-querybuilder [enableNotCondition]="true" [rule]="importRules">
</ejs-querybuilder>In the rule model, the NOT state is stored as not: true on the group:
public importRules = {
condition: 'and',
not: true, // entire group is negated
rules: [
{ field: 'Country', operator: 'equal', value: 'USA' }
]
};---
Common Combinations
Feature-rich Query Builder
<ejs-querybuilder
[allowDragAndDrop]="true"
[enableNotCondition]="true"
[enableSeparateConnector]="false"
[maxGroupCount]="3"
[showButtons]="showButtons"
[rule]="importRules">
<e-columns>
<!-- columns -->
</e-columns>
</ejs-querybuilder>public showButtons = {
ruleDelete: true, groupInsert: true, groupDelete: true,
cloneRule: true, cloneGroup: true, lockRule: true, lockGroup: true
};---
Troubleshooting
| Issue | Solution |
|---|---|
| Clone/lock buttons not visible | Set cloneRule: true / lockRule: true in showButtons |
addRules not working | Verify target group ID exists; root is always 'querybuilder_group0' |
| Drag-and-drop not enabling | Confirm [allowDragAndDrop]="true" on the <ejs-querybuilder> element |
maxGroupCount not preventing groups | Ensure value is set before initial render |
| Separate connector AND/OR missing | When enableSeparateConnector is true, group-level connector is hidden by design |
Getting Started — Syncfusion Angular Query Builder
Set up and render the Query Builder component in an Angular standalone application.
---
Dependencies
The Query Builder relies on several Syncfusion packages, all installed automatically with ng add:
@syncfusion/ej2-angular-querybuilder
├── @syncfusion/ej2-angular-base
├── @syncfusion/ej2-querybuilder
├── @syncfusion/ej2-base
├── @syncfusion/ej2-buttons
├── @syncfusion/ej2-data
├── @syncfusion/ej2-dropdowns
├── @syncfusion/ej2-calendars
├── @syncfusion/ej2-inputs
└── @syncfusion/ej2-splitbuttons---
Step 1: Create an Angular Application
Install Angular CLI (if not already installed):
npm install -g @angular/cliCreate a new application:
ng new syncfusion-angular-app
cd syncfusion-angular-appAngular 21+: Standalone components are the default. This guide uses standalone architecture.
In Angular 20+, the CLI generatesapp.ts,app.html,app.css(no.component.suffix).
In Angular 19 and below, files areapp.component.ts,app.component.html, etc.
---
Step 2: Install the Query Builder Package
Use ng add to install and auto-configure the package:
ng add @syncfusion/ej2-angular-querybuilderThis command automatically:
- Adds
@syncfusion/ej2-angular-querybuilderand peer dependencies topackage.json - Imports the component into your application
- Registers the default Material3 theme in
angular.json
For legacy Angular apps (Angular 15 and below using ngcc):
npm add @syncfusion/ej2-angular-querybuilder@32.1.19-ngcc---
Step 3: Add CSS / Theme Reference
The Material3 theme is auto-added to styles.css when using ng add. To manually style only the Query Builder:
/* styles.css */
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-angular-querybuilder/styles/material3.css";Import order matters — follow the component dependency sequence above.
Available themes: material3, material, bootstrap5, fabric, tailwind, fluent2
---
Step 4: Add the Query Builder Component
`src/app/app.ts` (Angular 20+ standalone):
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder';
import { Component } from '@angular/core';
@Component({
imports: [QueryBuilderModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-querybuilder width="70%">
<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="TitleOfCourtesy" label="Title Of Courtesy" type="string" [values]="values"></e-column>
<e-column field="Title" label="Title" type="string"></e-column>
<e-column field="HireDate" label="Hire Date" type="date" format="dd/MM/yyyy"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
<e-column field="City" label="City" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
`
})
export class App {
public values: string[] = ['Mr.', 'Mrs.'];
}`src/main.ts`:
import { bootstrapApplication } from '@angular/platform-browser';
import { App } from './app/app';
import 'zone.js';
bootstrapApplication(App).catch((err) => console.error(err));Column types:'string','number','date','boolean'
Use [values] on string columns to render a dropdown instead of a free-text input.---
Step 5: Run the Application
ng serveOpen http://localhost:4200 to see the Query Builder. You should see a rule interface with field, operator, and value dropdowns.
---
Rendering with an Initial Rule
Use the [rule] property to pre-populate the Query Builder with conditions on load:
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder';
import { Component } from '@angular/core';
@Component({
imports: [QueryBuilderModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-querybuilder width="70%" [rule]="importRules">
<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="Title" label="Title" type="string"></e-column>
<e-column field="HireDate" label="Hire Date" type="date" format="dd/MM/yyyy"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
`
})
export class App {
public importRules = {
condition: 'and',
rules: [
{
label: 'Employee ID', field: 'EmployeeID',
type: 'number', operator: 'equal', value: 1
},
{
label: 'Title', field: 'Title',
type: 'string', operator: 'equal', value: 'Sales Manager'
}
]
};
}---
Troubleshooting
| Issue | Solution |
|---|---|
| Component not rendering | Ensure QueryBuilderModule is in imports array of the standalone component |
| Styles missing | Check import order in styles.css; all dependencies must be imported before the querybuilder style |
zone.js error | Add import 'zone.js' to main.ts |
| ngcc warnings | Use Ivy-compatible package; ngcc is not supported in Angular 16+ |
| Columns empty | Ensure each <e-column> has a field property matching the dataSource keys |
Import & Export — Syncfusion Angular Query Builder
Import pre-built query conditions into the Query Builder, and export constructed rules to JSON, SQL, or MongoDB formats for use in APIs, databases, or saved search persistence.
---
Table of Contents
- Importing Rules
- From JSON (Initial Render)
- From JSON (Runtime)
- From Inline SQL
- From Parameter SQL
- From Named Parameter SQL
- From MongoDB Query
- Exporting Rules
- To JSON
- To Inline SQL
- To Parameter SQL
- To Named Parameter SQL
- To MongoDB Query
- Common Patterns
---
Importing Rules
From JSON (Initial Render)
Use the [rule] property binding to pre-populate the Query Builder when the component first renders:
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder';
import { Component } from '@angular/core';
@Component({
imports: [QueryBuilderModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-querybuilder width="70%" [rule]="importRules">
<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="Title" label="Title" type="string"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
`
})
export class App {
public importRules = {
condition: 'and',
rules: [
{
label: 'Employee ID', field: 'EmployeeID',
type: 'number', operator: 'equal', value: 1
},
{
condition: 'or',
rules: [
{ label: 'Country', field: 'Country', type: 'string', operator: 'equal', value: 'USA' },
{ label: 'Title', field: 'Title', type: 'string', operator: 'contains', value: 'Manager' }
]
}
]
};
}Rule model structure:
{
condition: 'and' | 'or', // group connector
not?: boolean, // negate the group
rules: Array<
// Individual condition:
{ field: string, label: string, type: string, operator: string, value: any }
// OR nested group:
| { condition: string, rules: [...] }
>
}---
From JSON (Runtime)
Use setRules() to update the Query Builder after it has already rendered:
import { QueryBuilderComponent } from '@syncfusion/ej2-angular-querybuilder';
import { ViewChild, Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<ejs-querybuilder #qb width="70%">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>
<button (click)="loadSavedQuery()">Load Saved Query</button>
`
})
export class App {
@ViewChild('qb') qb!: QueryBuilderComponent;
loadSavedQuery(): void {
this.qb.setRules({
condition: 'and',
rules: [
{ field: 'Country', label: 'Country', type: 'string', operator: 'equal', value: 'UK' },
{ field: 'EmployeeID', label: 'Employee ID', type: 'number', operator: 'greaterthan', value: 5 }
]
});
}
}---
From Inline SQL
Convert an inline SQL WHERE clause string directly into Query Builder rules:
loadFromSql(): void {
// SQL format: field operator 'value'
this.qb.setRulesFromSql("EmployeeID = 1 AND Country = 'USA'");
}// More complex SQL with nested conditions
const sql = "(EmployeeID > 5 AND Country = 'USA') OR Title LIKE '%Manager%'";
this.qb.setRulesFromSql(sql);---
From Parameter SQL
Import parameterized SQL (values as ? placeholders) with an accompanying values array:
loadFromParameterSql(): void {
this.qb.setParameterizedSql({
sql: "EmployeeID = ? AND Country = ?",
values: [1, 'USA']
});
}---
From Named Parameter SQL
Import named-parameter SQL (values as :name placeholders) with a named values object:
loadFromNamedParameterSql(): void {
this.qb.setParameterizedNamedSql({
sql: "EmployeeID = :empId AND Country = :country",
values: { empId: 1, country: 'USA' }
});
}---
From MongoDB Query
Import a MongoDB query string into the Query Builder:
loadFromMongo(): void {
const mongoQuery = '{"$and":[{"EmployeeID":{"$eq":1}},{"Country":{"$eq":"USA"}}]}';
this.qb.setMongoQuery(mongoQuery);
}---
Exporting Rules
To JSON
Get the current rules as a structured JSON object. Use this for saving queries or passing to DataManager.getPredicate():
exportToJson(): void {
const rules = this.qb.getRules();
console.log(JSON.stringify(rules, null, 2));
// Save to backend, localStorage, etc.
localStorage.setItem('savedQuery', JSON.stringify(rules));
}Example output:
{
"condition": "and",
"rules": [
{ "field": "EmployeeID", "label": "Employee ID", "type": "number", "operator": "equal", "value": 1 },
{ "field": "Country", "label": "Country", "type": "string", "operator": "equal", "value": "USA" }
]
}---
To Inline SQL
Export the current rules as an inline SQL WHERE clause string:
exportToSql(): void {
const rules = this.qb.getRules();
const sql = this.qb.getSqlFromRules(rules);
console.log(sql);
// Output: "EmployeeID = 1 AND Country = 'USA'"
}---
To Parameter SQL
Export as parameterized SQL with ? placeholders and a separate values array:
exportToParamSql(): void {
const rules = this.qb.getRules();
const result = this.qb.getParameterizedSql(rules);
console.log(result.sql); // "EmployeeID = ? AND Country = ?"
console.log(result.values); // [1, 'USA']
}Use the values array with prepared statements to prevent SQL injection.
---
To Named Parameter SQL
Export as named-parameter SQL with :name placeholders and a named values map:
exportToNamedParamSql(): void {
const rules = this.qb.getRules();
const result = this.qb.getParameterizedNamedSql(rules);
console.log(result.sql); // "EmployeeID = :EmployeeID AND Country = :Country"
console.log(result.values); // { EmployeeID: 1, Country: 'USA' }
}---
To MongoDB Query
Export as a MongoDB query string for direct use with MongoDB drivers or APIs:
exportToMongo(): void {
const rules = this.qb.getRules();
const mongoQuery = this.qb.getMongoQuery(rules);
console.log(mongoQuery);
// Output: '{"$and":[{"EmployeeID":{"$eq":1}},{"Country":{"$eq":"USA"}}]}'
}---
Common Patterns
Save and Restore Query (localStorage)
// Save current query
saveQuery(): void {
const rules = this.qb.getRules();
localStorage.setItem('myQuery', JSON.stringify(rules));
}
// Restore saved query
loadQuery(): void {
const saved = localStorage.getItem('myQuery');
if (saved) {
this.qb.setRules(JSON.parse(saved));
}
}Send SQL to Backend API
import { HttpClient } from '@angular/common/http';
applyFilter(): void {
const rules = this.qb.getRules();
const { sql, values } = this.qb.getParameterizedSql(rules);
this.http.post('/api/filter', { sql, values }).subscribe(data => {
this.results = data;
});
}Full Export Comparison
showAllFormats(): void {
const rules = this.qb.getRules();
console.log('JSON:', JSON.stringify(rules));
console.log('Inline SQL:', this.qb.getSqlFromRules(rules));
console.log('Param SQL:', this.qb.getParameterizedSql(rules));
console.log('Named SQL:', this.qb.getParameterizedNamedSql(rules));
console.log('MongoDB:', this.qb.getMongoQuery(rules));
}---
Troubleshooting
| Issue | Solution |
|---|---|
setRulesFromSql not parsing correctly | Ensure SQL string uses standard operators and quoted string values |
getRules() returns empty | Check that at least one complete condition (field + operator + value) exists |
| MongoDB export format unexpected | Verify field names in rules match actual dataSource field keys exactly |
| Parameter SQL values order wrong | Values correspond positionally to ? in the SQL — matches rule order |
Runtime setRules not updating UI | Call inside Angular change detection; use NgZone.run() if called outside Angular |
Style and Appearance — Syncfusion Angular Query Builder
Customize the Query Builder's visual appearance using CSS overrides, themes, and layout configuration properties.
---
CSS Class Reference
Override these classes to customize the Query Builder's appearance without replacing its default functionality:
| CSS Class | What It Targets |
|---|---|
.e-group-header .e-btn | Condition (AND/OR) button in the group header |
.e-group-body .e-rule-container | Rule container (each condition row) |
.e-group-container .e-group-header .e-dropdown-btn | Add Group/Condition dropdown button |
.e-query-builder .e-group-header .e-deletegroup | Delete Group button |
.e-query-builder .e-rule-field .e-rule-value-delete .e-rule-delete | Delete Condition button |
.e-query-builder .e-rule-list > ::after, .e-query-builder .e-rule-list > ::before | Group joining connector lines |
.e-query-builder .e-rule-container.e-joined-rule | Condition joining line between rules |
Example: Custom Color Scheme
/* styles.css */
/* Make condition button blue */
.e-group-header .e-btn {
background-color: #1976D2;
color: white;
border-radius: 4px;
}
/* Style rule containers with subtle background */
.e-group-body .e-rule-container {
background-color: #f9f9f9;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 8px;
}
/* Make delete buttons red */
.e-query-builder .e-rule-field .e-rule-value-delete .e-rule-delete {
color: #d32f2f;
}
/* Custom connector lines */
.e-query-builder .e-rule-list > ::before {
border-color: #1976D2;
}---
Themes
The Query Builder supports all Syncfusion themes. Switch themes by changing the CSS import in styles.css:
/* Material 3 (default with ng add) */
@import "../node_modules/@syncfusion/ej2-angular-querybuilder/styles/material3.css";
/* Bootstrap 5 */
@import "../node_modules/@syncfusion/ej2-angular-querybuilder/styles/bootstrap5.css";
/* Fluent 2 */
@import "../node_modules/@syncfusion/ej2-angular-querybuilder/styles/fluent2.css";
/* Tailwind CSS */
@import "../node_modules/@syncfusion/ej2-angular-querybuilder/styles/tailwind.css";
/* High Contrast (accessibility) */
@import "../node_modules/@syncfusion/ej2-angular-querybuilder/styles/highcontrast.css";Always import the matching theme CSS for all dependent packages (ej2-base,ej2-buttons,ej2-dropdowns, etc.) before the querybuilder theme.
Theme Studio
For fully custom themes, use Syncfusion Theme Studio to: 1. Adjust colors, fonts, and spacing interactively 2. Download a custom CSS file 3. Replace the default theme import with your custom file
---
Display Modes
Switch between horizontal (default) and vertical layout orientations using displayMode:
<!-- Horizontal layout (default) — fields, operators, values side by side -->
<ejs-querybuilder [displayMode]="'Horizontal'">
</ejs-querybuilder>
<!-- Vertical layout — fields, operators, values stacked vertically -->
<ejs-querybuilder [displayMode]="'Vertical'">
</ejs-querybuilder>// Toggle display mode dynamically
public currentMode: string = 'Horizontal';
toggleLayout(): void {
this.currentMode = this.currentMode === 'Horizontal' ? 'Vertical' : 'Horizontal';
}Use vertical mode in mobile/responsive layouts or when column labels are long.
---
Summary View
Enable a human-readable text summary of the current query below the rule editor:
<ejs-querybuilder [summaryView]="true" [rule]="importRules">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>The summary view displays a plain-English description like:
Employee ID Equal 1 AND Country Equal 'USA'
Summary view is hidden by default (summaryView: false). Enable it for end-user review or read-only display scenarios.---
RTL Support
Enable right-to-left layout for Arabic, Farsi, Urdu, and other RTL languages:
<ejs-querybuilder [enableRtl]="true" [rule]="importRules">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>When enabled, the component layout flips:
- Text reads right to left
- Icons and buttons mirror horizontally
- Dropdown popups open to the left
---
State Persistence
Persist the Query Builder's current rules to the browser's localStorage, so they survive page refresh or navigation:
<ejs-querybuilder [enablePersistence]="true" id="querybuilder">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"></e-column>
<e-column field="Country" label="Country" type="string"></e-column>
</e-columns>
</ejs-querybuilder>The id attribute is used as the localStorage key. The rule object is automatically saved on any change and restored on reload.
Always set a uniqueidwhen usingenablePersistenceto avoid collisions between multiple Query Builder instances.
---
Sort Columns
Control the order in which fields appear in the field selector dropdown:
<!-- Ascending alphabetical order -->
<ejs-querybuilder [sortDirection]="'Ascending'">
</ejs-querybuilder>
<!-- Descending order -->
<ejs-querybuilder [sortDirection]="'Descending'">
</ejs-querybuilder>By default, fields appear in their definition order. Use sortDirection to help users find fields faster in large column sets.---
Responsive Design Tips
/* Compact rule rows on small screens */
@media (max-width: 600px) {
.e-group-body .e-rule-container {
flex-direction: column;
}
.e-rule-filter, .e-rule-operator, .e-rule-value {
width: 100% !important;
}
}Combined with displayMode="Vertical" and maxGroupCount="2", this creates a mobile-friendly query builder.
---
Troubleshooting
| Issue | Solution |
|---|---|
| Theme not applying | Ensure all dependency CSS imports are in correct order in styles.css |
| Custom CSS not overriding | Add higher specificity or use !important as a last resort |
| RTL partially working | Wrap the app in dir="rtl" at the HTML level for full RTL support |
| State not persisting | Verify id attribute is set on <ejs-querybuilder> when using enablePersistence |
| Summary view not visible | Set [summaryView]="true" (boolean binding, not string "true") |
Templates & Model Binding — Syncfusion Angular Query Builder
Customize the Query Builder's UI by replacing default elements with custom Angular components. Templates let you control headers, column value inputs, and entire rule layouts. Model binding lets you configure the underlying Syncfusion input components (dropdowns, number boxes, etc.) used by the Query Builder.
---
Table of Contents
---
Header Template
Replace the default group header (AND/OR buttons + add/delete) with a fully custom interface. Use the #headerTemplate template variable on an <ng-template> inside <ejs-querybuilder>.
The custom header receives a data context object with:
data.ruleID— the ID of the current group (e.g.,querybuilder_group0)data.condition— current connector ('and'or'or')data.notCondition— current NOT state (only present ifenableNotConditionis true)
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder';
import { CheckBoxModule } from '@syncfusion/ej2-angular-buttons';
import { DropDownListModule } from '@syncfusion/ej2-angular-dropdowns';
import { DropDownButtonModule } from '@syncfusion/ej2-angular-splitbuttons';
import { Component } from '@angular/core';
@Component({
imports: [QueryBuilderModule, CheckBoxModule, DropDownListModule, DropDownButtonModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-querybuilder #querybuilder id="querybuilder" width="100%"
[rule]="importRules" [enableNotCondition]="true"
(actionBegin)="actionBegin($event)">
<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="Country" label="Country" type="string"></e-column>
</e-columns>
<ng-template #headerTemplate let-data>
<div class="e-groupheader">
<!-- NOT checkbox (only shown when group has notCondition) -->
<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>
<!-- AND/OR selector -->
<ejs-dropdownlist
id="{{data.ruleID}}_cndtn"
[dataSource]="conditionData"
[value]="data.condition"
[fields]="fields"
cssClass="e-custom-group-btn"
(change)="conditionChange($event)">
</ejs-dropdownlist>
<!-- Add Rule/Group split button -->
<button
ejs-dropdownbutton
id="{{data.ruleID}}_addbtn"
[items]="addButtonItems"
cssClass="e-round e-small e-caret-hide e-add-btn"
iconCss="e-icons e-add-icon"
(select)="onSelect($event)">
</button>
<!-- Delete Group button (not shown on root group) -->
<button
ejs-button
*ngIf="data.ruleID !== 'querybuilder_group0'"
id="{{data.ruleID}}_dltbtn"
class="e-btn e-delete-btn 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>
`
})
export class App {
public conditionData = [{ text: 'AND', value: 'and' }, { text: 'OR', value: 'or' }];
public fields = { text: 'text', value: 'value' };
public addButtonItems = [{ text: 'Add Condition' }, { text: 'Add Group' }];
public importRules = {
condition: 'and',
rules: [
{ field: 'EmployeeID', label: 'EmployeeID', type: 'number', operator: 'equal', value: 1 }
]
};
actionBegin(args: any): void {
if (args.requestType === 'header-template-create') {
// Initialize custom header controls here if needed
}
}
conditionChange(args: any): void { /* handle condition change */ }
onChange(args: any): void { /* handle NOT checkbox change */ }
onSelect(args: any): void { /* handle add rule/group selection */ }
onClick(args: any): void { /* handle delete group click */ }
}TheactionBeginevent withrequestType === 'header-template-create'fires when the header template is being initialized. Use it to bind event handlers or set initial values on custom controls.
---
Column Template
Define custom input widgets for individual columns using the template property. The template must implement three lifecycle functions:
| Function | Purpose |
|---|---|
create() | Create and return the DOM element for the custom widget |
write(args) | Initialize the widget and bind the current rule value |
destroy(args) | Clean up the widget instance to prevent memory leaks |
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder';
import { DropDownList } from '@syncfusion/ej2-dropdowns';
import { Component } from '@angular/core';
@Component({
imports: [QueryBuilderModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-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"
[template]="paymentTemplate">
</e-column>
</e-columns>
</ejs-querybuilder>
`
})
export class App {
public paymentTemplate = {
create: () => {
const elem = document.createElement('input');
elem.setAttribute('type', 'text');
return elem;
},
write: (args: any) => {
const dropDownObj = new DropDownList({
dataSource: ['Cash', 'Debit Card', 'Credit Card', 'Net Banking'],
value: args.values,
change: (e: any) => {
// Update the rule value when selection changes
args.updateRules(e.element, e.value);
}
});
dropDownObj.appendTo(args.elements);
// Store instance for cleanup
(args.elements as any).__dropDown = dropDownObj;
},
destroy: (args: any) => {
const dropDown = (args.elements as any).__dropDown;
if (dropDown) { dropDown.destroy(); }
}
};
public importRules = {
condition: 'and',
rules: [
{ field: 'PaymentMode', label: 'Payment Mode', type: 'string', operator: 'equal', value: 'Cash' }
]
};
}---
Column NgTemplate
Use Angular NgTemplate for column value inputs — a cleaner, Angular-native approach:
<ejs-querybuilder id="querybuilder" #querybuilder width="100%" [rule]="importRules">
<e-columns>
<e-column field="Category" label="Category" type="string"></e-column>
<!-- Column with NgTemplate for value input -->
<e-column field="PaymentMode" label="Payment Mode" type="string" [operators]="paymentOperators">
<ng-template #template let-data>
<ejs-dropdownlist
[dataSource]="paymentModes"
[value]="data.rule.value"
(change)="paymentChange($event, data.ruleID)">
</ejs-dropdownlist>
</ng-template>
</e-column>
<!-- Column with checkbox NgTemplate -->
<e-column field="TransactionType" label="Transaction Type" type="string" [operators]="boolOperators">
<ng-template #template let-data>
<ejs-checkbox
label="Is Expense"
[checked]="data.rule.value === 'Expense'"
(change)="transactionChange($event, data.ruleID)">
</ejs-checkbox>
</ng-template>
</e-column>
<e-column field="Amount" label="Amount" type="number"></e-column>
</e-columns>
</ejs-querybuilder>export class App {
@ViewChild('querybuilder') qb!: QueryBuilderComponent;
public paymentModes = ['Cash', 'Debit Card', 'Credit Card', 'Net Banking'];
public paymentOperators = [{ value: 'equal', text: 'Equal' }, { value: 'notequal', text: 'Not Equal' }];
public boolOperators = [{ value: 'equal', text: 'Equal' }];
paymentChange(args: any, ruleID: string): void {
this.qb.notifyChange(args.value, args.element, 'value');
}
transactionChange(args: any, ruleID: string): void {
const value = args.checked ? 'Expense' : 'Income';
this.qb.notifyChange(value, args.element, 'value');
}
}Use this.qb.notifyChange(value, element, 'value') to update the internal rule model when a custom NgTemplate input changes.---
Rule Template
A rule template replaces the entire rule row (field selector + operator + value) with a completely custom layout. Use ruleTemplate on a column plus handle initialization via actionBegin.
<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>
<!-- Custom rule template for Age 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">
<!-- Custom field selector -->
<div class="e-rule-filter">
<ejs-dropdownlist
[fields]="data.fields"
[dataSource]="data.columns"
[value]="data.rule.field"
(change)="fieldChange($event)">
</ejs-dropdownlist>
</div>
<!-- Custom value input: slider instead of number box -->
<div *ngIf="data.rule.type === 'number'" class="e-rule-value">
<ejs-slider
[value]="data.rule.value"
min="18" max="65"
id="{{data.ruleID}}_valuekey0"
(change)="valueChange($event, data.ruleID)">
</ejs-slider>
</div>
<!-- Rule action buttons -->
<div class="e-rule-btn">
<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>
</ng-template>
</e-column>
</e-columns>
</ejs-querybuilder>export class App {
@ViewChild('querybuilder') qb!: QueryBuilderComponent;
actionBegin(args: any): void {
if (args.requestType === 'template-initialize') {
// Set default operator for rule template columns
args.rule.operator = 'greaterthanorequal';
}
}
fieldChange(args: any): void {
this.qb.notifyChange(args.value, args.element, 'field');
}
valueChange(args: any, ruleID: string): void {
const elem = document.getElementById(`${ruleID}_valuekey0`);
this.qb.notifyChange(args.value, elem, 'value');
}
}The #ruleTemplate template variable identifies the NgTemplate as a rule template for its parent column.---
Model Binding
Model binding lets you configure the Syncfusion input components the Query Builder uses internally for field, operator, and value selectors. This is useful for enabling search/filtering in the dropdowns or customizing their appearance.
<ejs-querybuilder
[fieldModel]="fieldModel"
[operatorModel]="operatorModel"
[valueModel]="valueModel"
[rule]="importRules">
<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="Age" label="Age" type="number"></e-column>
</e-columns>
</ejs-querybuilder>// Enable filtering in field and operator dropdowns
public fieldModel = { allowFiltering: true, popupHeight: '400px' };
public operatorModel = { allowFiltering: true, popupHeight: '500px' };
// Customize value inputs by type
public valueModel = {
numericTextBoxModel: { cssClass: 'e-custom', min: 0, max: 100 },
multiSelectModel: { cssClass: 'e-custom', mode: 'CheckBox' },
datePickerModel: { cssClass: 'e-custom', format: 'dd/MM/yyyy' },
textBoxModel: { cssClass: 'e-custom', placeholder: 'Enter value' },
radioButtonModel: { cssClass: 'e-custom' }
};valueModel sub-properties:
| Property | Applies to | Purpose |
|---|---|---|
numericTextBoxModel | type="number" columns | Configure the NumericTextBox |
multiSelectModel | [values] string columns with in/notin | Configure the MultiSelect |
datePickerModel | type="date" columns | Configure the DatePicker |
textBoxModel | type="string" free-text columns | Configure the TextBox |
radioButtonModel | type="boolean" columns | Configure the RadioButton |
---
Troubleshooting
| Issue | Solution |
|---|---|
| Header template not rendering | Ensure the <ng-template #headerTemplate> is a direct child of <ejs-querybuilder> |
notifyChange not updating rule | Pass the exact DOM element from the template let-data context |
Rule template actionBegin not firing | Confirm (actionBegin) is bound on <ejs-querybuilder> and check args.requestType |
| Column template value not persisting | Implement the write function to set initial value from args.values |
| Dropdown in NgTemplate not responding | Call this.qb.notifyChange() in the change event handler |