Drag and Drop in Angular Query Builder UI
1 Sep 20269 minutes to read
Reorder rules and groups within the Query Builder by dragging and dropping them to new positions. This intuitive approach simplifies query construction and modification.
Drag and drop is disabled by default. Enable it by setting the allowDragAndDrop property to true. The following snippet shows the minimal markup required to enable drag and drop:
<ejs-querybuilder width="70%" [dataSource]="data" [rule]="importRules" allowDragAndDrop="true">
<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-columns>
</ejs-querybuilder>Events
The Query Builder emits three events during a drag-and-drop operation. Each event argument exposes a cancel property that can be set to true to prevent the corresponding action:
| Event | Triggers | Arguments |
|---|---|---|
dragStart |
When the user starts dragging a rule or group. |
dragRuleID: id of the dragged rule.dragGroupID: id of the group containing the dragged rule.cancel: set to true to cancel the drag. |
drag |
Continuously while the user drags a rule or group. |
dragRuleID: id of the dragged rule.dragGroupID: id of the group containing the dragged rule.cancel: set to true to cancel the drag. |
drop |
When the user drops a dragged rule or group onto a target. |
dropRuleID: id of the target rule.dropGroupID: id of the target group.cancel: set to true to cancel the drop. |
Bind the event handlers in the component template and handle the arguments in the component class:
<ejs-querybuilder width="70%" [dataSource]="data" [rule]="importRules"
allowDragAndDrop="true"
(dragStart)="onDragStart($event)"
(drag)="onDrag($event)"
(drop)="onDrop($event)">
</ejs-querybuilder>public onDragStart(args: { dragRuleID: string; dragGroupID: string; cancel: boolean }): void {
// Inspect dragRuleID / dragGroupID or cancel the drag
// args.cancel = true;
}
public onDrag(args: { dragRuleID: string; dragGroupID: string; cancel: boolean }): void {
// Inspect the in-progress drag or cancel it
// args.cancel = true;
}
public onDrop(args: { dropRuleID: string; dropGroupID: string; cancel: boolean }): void {
// Inspect dropRuleID / dropGroupID or cancel the drop
// args.cancel = true;
}The dragRuleID, dragGroupID, dropRuleID, and dropGroupID values are the element ids the Query Builder generates for each rule and group (for example, querybuilder_group0 and querybuilder_group0_rule0). Use these ids to identify the source and target of a drag-and-drop operation in your handler logic.
A group cannot be dropped into its own descendant group. Rules and groups can be reordered within the same group or moved between sibling groups, but they cannot be dropped at the root level outside the top-level group.
Sample
The following sample demonstrates drag and drop with rules and groups. The sample injects QueryLibrary (via QueryBuilderComponent.Inject(QueryLibrary)) and uses array values for the in operator (value: ['Davolio', 'Buchanan']) and the between operator (value: ["22/11/2023", "30/11/2023"]); ensure the QueryLibrary injection is included when reproducing the sample in your own application.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { QueryBuilderModule } from '@syncfusion/ej2-angular-querybuilder'
import { enableRipple } from '@syncfusion/ej2-base'
import { Component, ViewChild, OnInit } from '@angular/core';
import { RuleModel, QueryBuilderComponent } from '@syncfusion/ej2-angular-querybuilder';
import { QueryLibrary } from '@syncfusion/ej2-querybuilder';
import { employeeData } from './datasource';
QueryBuilderComponent.Inject(QueryLibrary);
@Component({
imports: [
QueryBuilderModule,
],
standalone: true,
selector: 'app-root',
template: `<!-- To render Query Builder. -->
<ejs-querybuilder #querybuilder class="row" width="70%" [dataSource]="data" [rule]="importRules" allowDragAndDrop="true">
<e-columns>
<e-column field="EmployeeID" label="Employee ID" type="number"></e-column>
<e-column field="FirstName" label="First Name" type="string"></e-column>
<e-column field="LastName" label="Title Of Courtesy" type="string"></e-column>
<e-column field="Age" label="Age" type="number"></e-column>
<e-column field="IsDeveloper" label="Is Developer" type="boolean"></e-column>
<e-column field="PrimaryFramework" label="Primary Framework" 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 AppComponent implements OnInit {
public data?: Object[];
public importRules?: RuleModel;
@ViewChild('querybuilder')
public qryBldrObj?: QueryBuilderComponent;
ngOnInit(): void {
this.data = employeeData;
this.importRules = {
condition: "and",
rules: [
{ label: "First Name", field: "FirstName", type: "string", operator: "startswith", value: "Andre" },
{ label: "Last Name", field: "LastName", type: "string", operator: "in", value: ['Davolio', 'Buchanan'] },
{ label: "Age", field: "Age", type: "number", operator: "greaterthan", value: 29 },
{
condition: "or", rules: [
{ label: "Is Developer", field: "IsDeveloper", type: "boolean", operator: "equal", value: true },
{ label: "Primary Framework", field: "PrimaryFramework", type: "string", operator: "equal", value: "React" }
]
},
{ label: "Hire Date", field: "HireDate", type: "date", operator: "between", value: ["22/11/2023", "30/11/2023"] },
],
};
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));