
Syncfusion Blazor Query Builder
- 233 installs
- 4 repo stars
- Updated July 28, 2026
- syncfusion/blazor-ui-components-skills
Use syncfusion-blazor-query-developer for development tasks
About
syncfusion-blazor-query-builder: A skill for development. This provides functionality for development workflows.
- syncfusion-blazor-query-builder
Syncfusion Blazor Query Builder by the numbers
- 233 all-time installs (skills.sh)
- +13 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #1,645 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/blazor-ui-components-skills --skill syncfusion-blazor-query-builderAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 233 |
|---|---|
| repo stars | ★ 4 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/blazor-ui-components-skills ↗ |
What it does
Use syncfusion-blazor-query-developer for development tasks
Files
Implementing Syncfusion Blazor Query Builder
The Syncfusion Blazor Query Builder component enables you to build dynamic, customizable query interfaces for filtering data with complex conditions, nested groups, and rule-based logic. It's ideal for advanced search UIs, business rule engines, and data filtering workflows.
Component Overview
The Query Builder organizes filtering logic into:
- Rules: Individual conditions (field = value)
- Groups: Collections of rules combined with AND/OR logic
- Operators: Comparison types (equal, contains, between, etc.)
- Columns: Data source fields that can be filtered
Key capabilities include programmatic rule/group management, event handling, drag-drop UI, state persistence, and extensive customization options.
Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation & setup for Blazor WebAssembly and Blazor Server
- NuGet package configuration
- Initial project setup
- Running your first Query Builder
Basic Setup & Configuration
📄 Read: references/basic-setup.md
- Creating the Query Builder component
- Defining columns and data types
- Configuring button visibility
- Setting default rules and initial state
Filtering, Rules & Groups (Core Feature)
📄 Read: references/filtering-and-rules.md
- Adding and deleting rules programmatically
- Creating and managing groups
- Operators and conditions
- Complex nested group patterns
- User interaction and validation
Data Binding & Filtered Results
📄 Read: references/data-binding.md
- Binding data sources to Query Builder
- Extracting and working with filtered records
- Dynamic data loading patterns
- Applying queries to datasets
Events & Callbacks (Core Feature)
📄 Read: references/events-and-callbacks.md
- Component lifecycle events (Created, Destroyed)
- Rule change tracking (RuleChanged, Changed)
- Pre-change validation (OnValueChange)
- Event-driven query updates
Customization & Styling
📄 Read: references/customization-and-styling.md
- CSS customization and theming
- Template support for custom UI
- Custom operator definitions
- Visual styling of groups and rules
Advanced Features
📄 Read: references/advanced-features.md
- Import/export queries (JSON, URL parameters)
- State persistence and local storage
- Drag-drop rule and group management
- Lock/clone group rules
- Read-only modes
- Query sorting and filtering
Localization & Accessibility
📄 Read: references/localization-and-accessibility.md
- Multi-language support (localization)
- Right-to-Left (RTL) support
- Accessibility best practices
- Keyboard navigation
- Screen reader support
Troubleshooting
📄 Read: references/troubleshooting.md
- Common configuration issues
- Event handling problems
- Data binding troubleshooting
- Performance optimization
- Testing strategies
Quick Start
Here's a minimal example to get started:
@using Syncfusion.Blazor.QueryBuilder
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="CustomerName" Label="Customer" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="OrderDate" Label="Date" Type="ColumnType.Date"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private List<Order> Orders { get; set; } = new()
{
new() { OrderID = 1, CustomerName = "Acme Corp", OrderDate = new(2024, 1, 15) },
new() { OrderID = 2, CustomerName = "Tech Ltd", OrderDate = new(2024, 2, 20) }
};
public class Order {
public int OrderID { get; set; }
public string CustomerName { get; set; }
public DateTime OrderDate { get; set; }
}
}Common Patterns
Add a Rule Programmatically
<SfButton OnClick="AddFilterRule">Add Rule</SfButton>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private async Task AddFilterRule() {
var rule = new RuleModel {
Label = "Order ID",
Field = "OrderID",
Type = "Number",
Operator = "equal",
Value = 1000
};
await QueryBuilder.AddRule(rule, "group0");
}
}Capture Query Changes
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderEvents TValue="Order" RuleChanged="OnRuleChanged"></QueryBuilderEvents>
</SfQueryBuilder>
@code {
private void OnRuleChanged(RuleChangeEventArgs args) {
Console.WriteLine($"Query changed: {args.RuleID}");
// Apply new filter or update results
}
}Export Query to JSON
<SfButton OnClick="ExportQuery">Export</SfButton>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private async Task ExportQuery() {
var queryJson = QueryBuilder.GetRules();
// Save or transmit queryJson
}
}Clone a Rule or Group
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<QueryBuilderShowButtons CloneGroup="true" CloneRule="true"></QueryBuilderShowButtons>
</SfQueryBuilder>
<SfButton OnClick="CloneRule">Clone Rule</SfButton>
<SfButton OnClick="CloneGroup">Clone Group</SfButton>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private void CloneRule() {
// Clone the first rule at position 1
QueryBuilder.CloneRule("group0_rule0", 1);
}
private void CloneGroup() {
// Clone group1 at position 2
QueryBuilder.CloneGroup("group1", 2);
}
}Lock a Rule or Group
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<QueryBuilderShowButtons LockGroup="true" LockRule="true"></QueryBuilderShowButtons>
</SfQueryBuilder>
<SfButton OnClick="LockRule">Lock Rule</SfButton>
<SfButton OnClick="UnlockRule">Unlock Rule</SfButton>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private void LockRule() {
// Lock the rule to prevent editing
QueryBuilder.LockRule("group0_rule0", true);
}
private void UnlockRule() {
// Unlock the rule to allow editing
QueryBuilder.LockRule("group0_rule0", false);
}
}Query Export Methods
The Query Builder provides built-in methods to export queries in different formats:
1. GetSqlFromRules - Export as Inline SQL
Convert query rules to standard SQL WHERE clause format:
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="CustomerName" Label="Customer" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
<SfButton OnClick="ExportInlineSQL">Get SQL Query</SfButton>
<p>@SqlQuery</p>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private string SqlQuery = "";
private void ExportInlineSQL() {
RuleModel rules = QueryBuilder.GetValidRules();
SqlQuery = QueryBuilder.GetSqlFromRules(rules);
// Output: "OrderID = 1000 AND CustomerName LIKE '%Acme%'"
}
public class Order {
public int OrderID { get; set; }
public string CustomerName { get; set; }
}
}2. GetParameterSql - Export as Parameterized SQL
Export SQL with indexed parameters to prevent SQL injection:
@using System.Text.Json
<SfButton OnClick="ExportParameterSQL">Get Parameterized SQL</SfButton>
<pre>@ParameterSqlJson</pre>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private string ParameterSqlJson = "";
private void ExportParameterSQL() {
RuleModel rules = QueryBuilder.GetValidRules();
ParameterSql paramSql = QueryBuilder.GetParameterSql(rules);
ParameterSqlJson = JsonSerializer.Serialize(paramSql, new JsonSerializerOptions {
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
// Access individual properties:
// paramSql.Sql - SQL string with ? placeholders
// paramSql.Parameters - List of parameter values
}
}Output Example:
{
"Sql": "OrderID = ? AND CustomerName LIKE ?",
"Parameters": [1000, "%Acme%"]
}3. GetNamedParameterSql - Export as Named Parameter SQL
Export SQL with named parameters (@param0, @param1, etc.):
@using System.Text.Json
<SfButton OnClick="ExportNamedParameterSQL">Get Named Parameter SQL</SfButton>
<pre>@NamedParameterSqlJson</pre>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private string NamedParameterSqlJson = "";
private void ExportNamedParameterSQL() {
RuleModel rules = QueryBuilder.GetValidRules();
NamedParameterSql namedParamSql = QueryBuilder.GetNamedParameterSql(rules);
NamedParameterSqlJson = JsonSerializer.Serialize(namedParamSql, new JsonSerializerOptions {
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
// Access individual properties:
// namedParamSql.Sql - SQL string with @param placeholders
// namedParamSql.Parameters - Dictionary of parameter names/values
}
}Output Example:
{
"Sql": "OrderID = @param0 AND CustomerName LIKE @param1",
"Parameters": {
"@param0": 1000,
"@param1": "%Acme%"
}
}4. GetMongoQuery - Export as MongoDB Query
Convert query rules to MongoDB query format:
@using System.Text.Json
<SfButton OnClick="ExportMongoQuery">Get MongoDB Query</SfButton>
<pre>@MongoQueryJson</pre>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private string MongoQueryJson = "";
private void ExportMongoQuery() {
RuleModel rules = QueryBuilder.GetValidRules();
string mongoQuery = QueryBuilder.GetMongoQuery(rules);
// Pretty print the JSON
JsonDocument jsonDoc = JsonDocument.Parse(mongoQuery);
MongoQueryJson = JsonSerializer.Serialize(jsonDoc.RootElement, new JsonSerializerOptions {
WriteIndented = true
});
}
}Output Example:
{
"$and": [
{ "OrderID": { "$eq": 1000 } },
{ "CustomerName": { "$regex": "Acme", "$options": "i" } }
]
}Import Query Methods
You can also import queries back into the Query Builder:
@code {
// Import from SQL
private void ImportSQL(string sqlQuery) {
QueryBuilder.SetRulesFromSql(sqlQuery);
}
// Import from Parameterized SQL
private void ImportParameterSQL(ParameterSql paramSql) {
QueryBuilder.SetParameterSql(paramSql);
}
// Import from Named Parameter SQL
private void ImportNamedParameterSQL(NamedParameterSql namedParamSql) {
QueryBuilder.SetNamedParameterSql(namedParamSql);
}
// Import from MongoDB Query
private void ImportMongoQuery(string mongoQuery) {
QueryBuilder.SetMongoQuery(mongoQuery);
}
}Complete Query Export/Import Example
@page "/query-export-demo"
@using Syncfusion.Blazor.QueryBuilder
@using Syncfusion.Blazor.Buttons
@using System.Text.Json
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="CustomerName" Label="Customer" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="OrderDate" Label="Order Date" Type="ColumnType.Date"></QueryBuilderColumn>
<QueryBuilderColumn Field="Amount" Label="Amount" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
<div class="button-group">
<SfButton OnClick="@(() => ExportQuery("SQL"))">Export SQL</SfButton>
<SfButton OnClick="@(() => ExportQuery("ParameterSQL"))">Export Parameter SQL</SfButton>
<SfButton OnClick="@(() => ExportQuery("NamedSQL"))">Export Named SQL</SfButton>
<SfButton OnClick="@(() => ExportQuery("MongoDB"))">Export MongoDB</SfButton>
</div>
<div class="output">
<h3>@OutputTitle</h3>
<pre>@OutputQuery</pre>
</div>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private string OutputQuery = "";
private string OutputTitle = "";
private void ExportQuery(string format) {
RuleModel rules = QueryBuilder.GetValidRules();
switch(format) {
case "SQL":
OutputTitle = "Inline SQL Query";
OutputQuery = QueryBuilder.GetSqlFromRules(rules);
break;
case "ParameterSQL":
OutputTitle = "Parameterized SQL Query";
ParameterSql paramSql = QueryBuilder.GetParameterSql(rules);
OutputQuery = JsonSerializer.Serialize(paramSql, new JsonSerializerOptions {
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
break;
case "NamedSQL":
OutputTitle = "Named Parameter SQL Query";
NamedParameterSql namedSql = QueryBuilder.GetNamedParameterSql(rules);
OutputQuery = JsonSerializer.Serialize(namedSql, new JsonSerializerOptions {
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
break;
case "MongoDB":
OutputTitle = "MongoDB Query";
string mongoQuery = QueryBuilder.GetMongoQuery(rules);
JsonDocument jsonDoc = JsonDocument.Parse(mongoQuery);
OutputQuery = JsonSerializer.Serialize(jsonDoc.RootElement, new JsonSerializerOptions {
WriteIndented = true
});
break;
}
}
public class Order {
public int OrderID { get; set; }
public string CustomerName { get; set; }
public DateTime OrderDate { get; set; }
public decimal Amount { get; set; }
}
}Key Features at a Glance
| Feature | Use Case | Reference |
|---|---|---|
| Drag-Drop Rules | Reorder conditions interactively | advanced-features.md |
| Clone Groups/Rules | Duplicate complex query structures | advanced-features.md |
| Lock Groups/Rules | Prevent editing of specific conditions | advanced-features.md |
| Nested Groups | Complex multi-condition logic | filtering-and-rules.md |
| Event Tracking | React to query changes | events-and-callbacks.md |
| State Persistence | Save/restore queries | advanced-features.md |
| Localization | Multi-language support | localization-and-accessibility.md |
| Custom Templates | Branded UI | customization-and-styling.md |
Next Steps
1. Start with references/getting-started.md to set up your environment 2. Follow references/basic-setup.md for component configuration 3. Explore references/filtering-and-rules.md for core filtering logic 4. Handle queries with references/events-and-callbacks.md 5. Refer to other guides as needed for advanced scenarios
Advanced Features
This guide covers import/export, state persistence, drag-drop, and other advanced Query Builder capabilities.
Table of Contents
- Import/Export Queries
- State Persistence
- Drag & Drop
- Lock & Clone Rules
- Read-Only Mode
- Sort & Filter Columns
Import/Export Queries
Export to JSON
Export the current query structure as JSON:
<SfButton OnClick="ExportQueryAsJson">Export Query</SfButton>
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private void ExportQueryAsJson() {
var rules = QueryBuilder.GetRules();
var json = JsonConvert.SerializeObject(rules, Formatting.Indented);
Console.WriteLine("Exported Query:");
Console.WriteLine(json);
// Example output:
// {
// "Condition": "and",
// "Rules": [
// {
// "Field": "OrderID",
// "Operator": "greater",
// "Value": 1000
// }
// ]
// }
}
}Import from JSON
Import a previously saved query:
<SfButton OnClick="ImportQueryFromJson">Load Saved Query</SfButton>
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<QueryBuilderRule Rules="@ImportedRules"></QueryBuilderRule>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private List<RuleModel> ImportedRules;
private void ImportQueryFromJson() {
var json = @"{
""Condition"": ""and"",
""Rules"": [
{
""Field"": ""OrderID"",
""Type"": ""Number"",
""Operator"": ""greater"",
""Value"": 1000
}
]
}";
var rules = JsonConvert.DeserializeObject<RuleModel>(json);
ImportedRules = rules.Rules ?? new List<RuleModel>();
StateHasChanged();
}
}Export/Import via URL
@code {
private void ExportToUrl() {
var rules = QueryBuilder.GetRules();
var queryString = BuildQueryString(rules);
var url = $"https://yoursite.com/search?{queryString}";
Console.WriteLine($"Shareable URL: {url}");
}
private string BuildQueryString(RuleModel rules) {
var sb = new StringBuilder();
sb.Append("condition=" + rules.Condition);
foreach (var rule in rules.Rules) {
sb.Append($"&field={rule.Field}&operator={rule.Operator}&value={rule.Value}");
}
return sb.ToString();
}
private void LoadFromUrl(string queryString) {
var parameters = System.Web.HttpUtility.ParseQueryString(queryString);
var rules = new List<RuleModel>();
for (int i = 0; i < parameters.Count; i += 3) {
rules.Add(new RuleModel {
Field = parameters[$"field{i/3}"],
Operator = parameters[$"operator{i/3}"],
Value = parameters[$"value{i/3}"]
});
}
ImportedRules = rules;
}
}State Persistence
Save to LocalStorage
@using System.Text.Json
<SfButton OnClick="SaveQueryToStorage">Save Query</SfButton>
<SfButton OnClick="LoadQueryFromStorage">Load Query</SfButton>
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private SfQueryBuilder<Order> QueryBuilder;
@inject IJSRuntime JS;
private async Task SaveQueryToStorage() {
var rules = QueryBuilder.GetRules();
var json = JsonConvert.SerializeObject(rules);
await JS.InvokeVoidAsync("localStorage.setItem", "savedQuery", json);
Console.WriteLine("Query saved to localStorage");
}
private async Task LoadQueryFromStorage() {
var json = await JS.InvokeAsync<string>("localStorage.getItem", "savedQuery");
if (!string.IsNullOrEmpty(json)) {
var rules = JsonConvert.DeserializeObject<RuleModel>(json);
// Apply loaded rules to Query Builder
Console.WriteLine("Query loaded from localStorage");
}
}
}Save to Database
@code {
private async Task SaveQueryToDatabase(string userId, string queryName) {
var rules = QueryBuilder.GetRules();
var json = JsonConvert.SerializeObject(rules);
var queryRecord = new SavedQuery {
UserId = userId,
QueryName = queryName,
QueryDefinition = json,
CreatedDate = DateTime.UtcNow
};
await _repository.SaveQueryAsync(queryRecord);
Console.WriteLine($"Query '{queryName}' saved to database");
}
private async Task LoadQueryFromDatabase(int savedQueryId) {
var queryRecord = await _repository.GetQueryAsync(savedQueryId);
if (queryRecord != null) {
var rules = JsonConvert.DeserializeObject<RuleModel>(queryRecord.QueryDefinition);
// Apply to Query Builder
}
}
public class SavedQuery {
public int Id { get; set; }
public string UserId { get; set; }
public string QueryName { get; set; }
public string QueryDefinition { get; set; }
public DateTime CreatedDate { get; set; }
}
}Drag & Drop
Enable Drag-Drop Interface
<SfQueryBuilder TValue="Order" AllowDragAndDrop="true">
<QueryBuilderShowButtons RuleDelete="true" GroupDelete="true"></QueryBuilderShowButtons>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>Benefits:
- Reorder rules and groups interactively
- Improve query building UX
- Visual feedback during drag operations
Programmatic Reordering
@code {
private async Task ReorderRules() {
// Get current rules
var rules = QueryBuilder.GetRules();
// Swap first two rules
if (rules.Rules.Count >= 2) {
var temp = rules.Rules[0];
rules.Rules[0] = rules.Rules[1];
rules.Rules[1] = temp;
}
}
}Lock & Clone Rules
Clone Groups and Rules
The Query Builder supports cloning both individual rules and entire groups. Cloning creates an exact duplicate of the selected rule or group adjacent to the original, making it quick to replicate complex query structures.
Enable Clone Buttons in UI:
@using Syncfusion.Blazor.QueryBuilder
@using Syncfusion.Blazor.Buttons
<SfQueryBuilder TValue="EmployeeDetails" @ref="QueryBuilder">
<QueryBuilderShowButtons RuleDelete="true" GroupDelete="true" CloneGroup="true" CloneRule="true"></QueryBuilderShowButtons>
<QueryBuilderRule Condition="or" Rules="@Rules"></QueryBuilderRule>
<QueryBuilderColumns>
<QueryBuilderColumn Field="EmployeeID" Label="Employee ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="FirstName" Label="First Name" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Country" Label="Country" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
SfQueryBuilder<EmployeeDetails> QueryBuilder;
List<RuleModel> Rules = new List<RuleModel>()
{
new RuleModel { Field="Country", Label="Country", Type="String", Operator="equal", Value = "England" },
new RuleModel { Field="EmployeeID", Label="EmployeeID", Type="Number", Operator="notequal", Value = 1001 }
};
public class EmployeeDetails
{
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string Country { get; set; }
}
}Programmatically Clone Group:
<SfButton OnClick="CloneGroup">Clone Group</SfButton>
@code {
private void CloneGroup()
{
// CloneGroup(groupId, insertIndex)
// First parameter: existing group's ID
// Second parameter: insert index within its parent
QueryBuilder.CloneGroup("group1", 2);
Console.WriteLine("Group cloned successfully");
}
}Programmatically Clone Rule:
<SfButton OnClick="CloneRule">Clone Rule</SfButton>
@code {
private void CloneRule()
{
// CloneRule(ruleId, insertIndex)
// First parameter: existing rule's ID
// Second parameter: insert index within its parent group
QueryBuilder.CloneRule("group0_rule0", 1);
Console.WriteLine("Rule cloned successfully");
}
}Note: Ensure that the IDs passed toCloneGroupandCloneRulerefer to existing items in the current model. The cloning buttons can be shown or hidden viaQueryBuilderShowButtons.
Lock Groups and Rules
The Query Builder supports locking individual rules or entire groups. When a rule is locked, its field, operator, and value editors are disabled and cannot be changed. Locking a group disables all editors and actions within that group. Locked items still participate in query evaluation; only editing is restricted.
Enable Lock Buttons in UI:
@using Syncfusion.Blazor.QueryBuilder
@using Syncfusion.Blazor.Buttons
<SfQueryBuilder TValue="EmployeeDetails" @ref="QueryBuilder">
<QueryBuilderShowButtons RuleDelete="true" GroupDelete="true" LockGroup="true" LockRule="true"></QueryBuilderShowButtons>
<QueryBuilderRule Condition="or" Rules="@Rules"></QueryBuilderRule>
<QueryBuilderColumns>
<QueryBuilderColumn Field="EmployeeID" Label="Employee ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="FirstName" Label="First Name" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Country" Label="Country" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
SfQueryBuilder<EmployeeDetails> QueryBuilder;
List<RuleModel> Rules = new List<RuleModel>()
{
new RuleModel { Field="Country", Label="Country", Type="String", Operator="equal", Value = "England" },
new RuleModel { Field="EmployeeID", Label="EmployeeID", Type="Number", Operator="notequal", Value = 1001 }
};
public class EmployeeDetails
{
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string Country { get; set; }
}
}Programmatically Lock Group:
<SfButton OnClick="LockGroup">Lock Group</SfButton>
@code {
private void LockGroup()
{
// LockGroup(groupId, isLocked)
// First parameter: group's ID
// Second parameter: true to lock, false to unlock
QueryBuilder.LockGroup("group0", true);
Console.WriteLine("Group locked successfully");
}
}Programmatically Lock Rule:
<SfButton OnClick="LockRule">Lock Rule</SfButton>
@code {
private void LockRule()
{
// LockRule(ruleId, isLocked)
// First parameter: rule's ID
// Second parameter: true to lock, false to unlock
QueryBuilder.LockRule("group0_rule0", true);
Console.WriteLine("Rule locked successfully");
}
}Complete Example with Lock and Clone:
@page "/lock-clone-demo"
@using Syncfusion.Blazor.QueryBuilder
@using Syncfusion.Blazor.Buttons
<SfQueryBuilder TValue="EmployeeDetails" @ref="QueryBuilder">
<QueryBuilderShowButtons
RuleDelete="true"
GroupDelete="true"
GroupInsert="true"
CloneGroup="true"
CloneRule="true"
LockGroup="true"
LockRule="true">
</QueryBuilderShowButtons>
<QueryBuilderRule Condition="or" Rules="@Rules"></QueryBuilderRule>
<QueryBuilderColumns>
<QueryBuilderColumn Field="EmployeeID" Label="Employee ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="FirstName" Label="First Name" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Country" Label="Country" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="HireDate" Label="Hire Date" Type="ColumnType.Date" Format="MM/dd/yyyy"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
<div class="button-group">
<SfButton OnClick="CloneGroup" IsPrimary="true">Clone Group</SfButton>
<SfButton OnClick="CloneRule" IsPrimary="true">Clone Rule</SfButton>
<SfButton OnClick="LockGroup" IsPrimary="true">Lock Group</SfButton>
<SfButton OnClick="LockRule" IsPrimary="true">Lock Rule</SfButton>
<SfButton OnClick="UnlockGroup" IsPrimary="true">Unlock Group</SfButton>
<SfButton OnClick="UnlockRule" IsPrimary="true">Unlock Rule</SfButton>
</div>
@code {
SfQueryBuilder<EmployeeDetails> QueryBuilder;
List<RuleModel> Rules = new List<RuleModel>()
{
new RuleModel { Field="Country", Label="Country", Type="String", Operator="equal", Value = "England" },
new RuleModel { Field="EmployeeID", Label="EmployeeID", Type="Number", Operator="notequal", Value = 1001 },
new RuleModel { Condition = "or", Rules = new List<RuleModel>()
{
new RuleModel { Field="FirstName", Label="FirstName", Type="String", Operator="startswith", Value = "John" }
}}
};
private void CloneGroup()
{
QueryBuilder.CloneGroup("group1", 2);
}
private void CloneRule()
{
QueryBuilder.CloneRule("group0_rule0", 1);
}
private void LockGroup()
{
QueryBuilder.LockGroup("group0", true);
}
private void LockRule()
{
QueryBuilder.LockRule("group0_rule0", true);
}
private void UnlockGroup()
{
QueryBuilder.LockGroup("group0", false);
}
private void UnlockRule()
{
QueryBuilder.LockRule("group0_rule0", false);
}
public class EmployeeDetails
{
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string Country { get; set; }
public DateTime HireDate { get; set; }
}
}Key Features:
- Clone Buttons: Show/hide via
QueryBuilderShowButtonspropertiesCloneGroupandCloneRule - Lock Buttons: Show/hide via
QueryBuilderShowButtonspropertiesLockGroupandLockRule - Methods Available:
CloneGroup(groupId, insertIndex)- Clone a group at specified positionCloneRule(ruleId, insertIndex)- Clone a rule at specified positionLockGroup(groupId, isLocked)- Lock/unlock a groupLockRule(ruleId, isLocked)- Lock/unlock a rule
Read-Only Mode
Disable Editing
<SfQueryBuilder TValue="Order" Readonly="true">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>Use cases:
- Display pre-defined queries
- Allow viewing without modification
- Preview mode
Restrict Specific Operations
@code {
private bool CanDeleteRules = false;
private bool CanAddGroups = true;
private bool CanModifyValues = true;
private async Task HandleRuleChange(RuleChangeEventArgs args) {
if (args.Action == "remove" && !CanDeleteRules) {
Console.WriteLine("Rule deletion not allowed");
args.Cancel = true;
}
}
}Sort & Filter Columns
Sort Columns in Dropdown
<SfQueryBuilder TValue="Order">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number" SortOrder="1"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String" SortOrder="2"></QueryBuilderColumn>
<QueryBuilderColumn Field="Amount" Label="Amount" Type="ColumnType.Number" SortOrder="3"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>Filter Available Columns
<SfQueryBuilder TValue="Order">
<QueryBuilderColumns>
<!-- Show only these columns -->
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number" Visible="true"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String" Visible="true"></QueryBuilderColumn>
<!-- Hide from UI -->
<QueryBuilderColumn Field="InternalNotes" Label="Internal" Type="ColumnType.String" Visible="false"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>Dynamic Column Visibility
<SfButton OnClick="ToggleColumnVisibility">Toggle Columns</SfButton>
@code {
private bool ShowInternalColumns = false;
private void ToggleColumnVisibility() {
ShowInternalColumns = !ShowInternalColumns;
StateHasChanged();
}
}Next Steps
- Localization - Multi-language and RTL support
- Troubleshooting - Performance and advanced issues
- Customization - Styling options
Basic Setup & Configuration
This guide covers creating and configuring the Query Builder component with columns, operators, and initial rules.
Creating the Component
The simplest Query Builder has a data source and columns:
@using Syncfusion.Blazor.QueryBuilder
<SfQueryBuilder TValue="Order">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="CustomerName" Label="Customer" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>Parameters:
TValue: Type of data model (e.g.,Order,Employee)Field: Property name in the data modelLabel: Display name in the UIType: Data type (String, Number, Date, Boolean)
Column Configuration
Column Types
<QueryBuilderColumns>
<!-- String columns -->
<QueryBuilderColumn Field="Name" Label="Name" Type="ColumnType.String"></QueryBuilderColumn>
<!-- Number columns -->
<QueryBuilderColumn Field="Age" Label="Age" Type="ColumnType.Number"></QueryBuilderColumn>
<!-- Date columns -->
<QueryBuilderColumn Field="BirthDate" Label="Birth Date" Type="ColumnType.Date" Format="MM/dd/yyyy"></QueryBuilderColumn>
<!-- Boolean columns (checkbox) -->
<QueryBuilderColumn Field="IsActive" Label="Active" Type="ColumnType.Boolean"></QueryBuilderColumn>
</QueryBuilderColumns>With Data Source
<SfQueryBuilder TValue="Employee" DataSource="@Employees">
<QueryBuilderColumns>
<QueryBuilderColumn Field="EmployeeID" Label="ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="FirstName" Label="First Name" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Department" Label="Department" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Salary" Label="Salary" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="JoinDate" Label="Join Date" Type="ColumnType.Date"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private List<Employee> Employees = new() {
new() { EmployeeID = 1, FirstName = "John", Department = "IT", Salary = 50000, JoinDate = new(2020, 1, 15) },
new() { EmployeeID = 2, FirstName = "Jane", Department = "HR", Salary = 45000, JoinDate = new(2019, 6, 20) }
};
public class Employee {
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string Department { get; set; }
public double Salary { get; set; }
public DateTime JoinDate { get; set; }
}
}Configuring Button Visibility
Control which action buttons appear in the UI:
<SfQueryBuilder TValue="Order">
<QueryBuilderShowButtons
RuleDelete="true"
GroupInsert="true"
GroupDelete="true">
</QueryBuilderShowButtons>
<QueryBuilderColumns>
<!-- columns -->
</QueryBuilderColumns>
</SfQueryBuilder>Available buttons:
RuleDelete- Delete individual rulesGroupDelete- Delete groupsGroupInsert- Add new groups
Setting Default Rules
Initialize the Query Builder with predefined rules:
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<QueryBuilderRule Condition="and" Rules="@DefaultRules"></QueryBuilderRule>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="OrderDate" Label="Order Date" Type="ColumnType.Date"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private List<RuleModel> DefaultRules = new() {
new RuleModel {
Label = "Order ID",
Field = "OrderID",
Type = "Number",
Operator = "greater",
Value = 1000
},
new RuleModel {
Label = "Status",
Field = "Status",
Type = "String",
Operator = "equal",
Value = "Pending"
}
};
public class Order {
public int OrderID { get; set; }
public string Status { get; set; }
public DateTime OrderDate { get; set; }
}
}Nested Groups
Create complex queries with nested groups (AND/OR logic):
<SfQueryBuilder TValue="Product" @ref="QueryBuilder">
<QueryBuilderRule Condition="and" Rules="@NestedRules"></QueryBuilderRule>
<QueryBuilderColumns>
<QueryBuilderColumn Field="Name" Label="Product Name" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Category" Label="Category" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Price" Label="Price" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="InStock" Label="In Stock" Type="ColumnType.Boolean"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private SfQueryBuilder<Product> QueryBuilder;
private List<RuleModel> NestedRules = new() {
new RuleModel {
Label = "Category",
Field = "Category",
Type = "String",
Operator = "equal",
Value = "Electronics"
},
new RuleModel {
Condition = "or", // Nested group with OR condition
Rules = new List<RuleModel> {
new RuleModel {
Label = "Price",
Field = "Price",
Type = "Number",
Operator = "lessthan",
Value = 100
},
new RuleModel {
Label = "In Stock",
Field = "InStock",
Type = "Boolean",
Operator = "equal",
Value = true
}
}
}
};
public class Product {
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
public bool InStock { get; set; }
}
}Available Operators by Type
String Operators
equal- Equalsnotequal- Not equalscontains- Containsnotcontains- Does not containstartswith- Starts withendswith- Ends within- In listnotin- Not in list
Number Operators
equal- Equalsnotequal- Not equalslessthan- Less thanlessthanorequal- Less than or equalgreaterthan- Greater thangreaterthanorequal- Greater than or equalbetween- Betweennotbetween- Not betweenin- In listnotin- Not in list
Date Operators
equal- Equalsnotequal- Not equalslessthan- Beforelessthanorequal- Before or ongreaterthan- Aftergreaterthanorequal- After or onbetween- Between datesnotbetween- Not between dates
Boolean Operators
equal- Is true/false
Component References
To programmatically access the Query Builder:
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<!-- columns -->
</SfQueryBuilder>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private async Task DoSomething() {
// Access methods on QueryBuilder reference
var rules = QueryBuilder.GetRules();
}
}Next Steps
- Filtering & Rules - Add/delete rules programmatically
- Data Binding - Bind and filter data
- Events & Callbacks - Handle query changes
Customization & Styling
This guide covers CSS customization, theming, templates, and styling options for the Query Builder component.
CSS Customization
Theme Selection
Choose a theme by linking the appropriate CSS file in your host page:
<!-- Bootstrap 5 Theme (Default) -->
<link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/bootstrap5.css">
<!-- Or use alternative themes -->
<link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/material3.css">
<link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/fluent2.css">
<link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/tailwindcss.css">Custom CSS Classes
Style Query Builder elements with custom CSS:
/* Custom Query Builder styling */
.e-querybuilder {
background-color: #f5f5f5;
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
}
/* Style rules */
.e-rule {
background-color: white;
border: 1px solid #e0e0e0;
border-radius: 4px;
margin-bottom: 10px;
padding: 10px;
}
/* Style groups */
.e-group {
background-color: #fafafa;
border-left: 4px solid #007bff;
border-radius: 4px;
padding: 12px;
margin-bottom: 10px;
}
/* Style buttons */
.e-querybuilder .e-addrule,
.e-querybuilder .e-addgroup {
background-color: #007bff;
color: white;
border-radius: 4px;
}
/* Operator dropdown */
.e-querybuilder .e-operator-select {
border-color: #e0e0e0;
}Inline Styles
Apply inline styles directly to components:
<SfQueryBuilder TValue="Order" Style="background-color: #fafafa; padding: 20px; border-radius: 8px;">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>Component Styling Classes
Available CSS Classes
| Class | Purpose |
|---|---|
.e-querybuilder | Main container |
.e-rule | Individual rule element |
.e-group | Group container |
.e-group-header | Group header (AND/OR selector) |
.e-field-select | Field dropdown |
.e-operator-select | Operator dropdown |
.e-value-field | Value input field |
.e-addrule | Add rule button |
.e-addgroup | Add group button |
.e-deletegroup | Delete group button |
.e-deleterule | Delete rule button |
Style Specific Elements
/* Custom field dropdown styling */
.e-querybuilder .e-field-select {
background-color: #e8f4f8;
border: 2px solid #007bff;
border-radius: 4px;
font-weight: 500;
}
/* Custom operator styling */
.e-querybuilder .e-operator-select {
background-color: white;
border: 1px solid #ddd;
}
/* Custom value input */
.e-querybuilder .e-value-field input {
background-color: #fffaf0;
border: 1px solid #e0a070;
padding: 8px;
}
/* Button styling */
.e-querybuilder .e-addrule,
.e-querybuilder .e-addgroup {
background-color: #28a745;
color: white;
border-radius: 4px;
padding: 8px 12px;
font-weight: 500;
}
.e-querybuilder .e-deletegroup,
.e-querybuilder .e-deleterule {
background-color: #dc3545;
color: white;
border-radius: 4px;
}Templates
Custom Field Template
@using Syncfusion.Blazor.QueryBuilder
<SfQueryBuilder TValue="Order">
<QueryBuilderTemplates>
<FieldTemplate>
<div style="padding: 8px;">
<strong>@context.Label</strong>
<p style="font-size: 12px; color: #666;">@context.Field</p>
</div>
</FieldTemplate>
</QueryBuilderTemplates>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>Custom Operator Template
<QueryBuilderTemplates>
<OperatorTemplate>
<select style="padding: 5px; border: 1px solid #ddd;">
<option value="equal">Is Equal To</option>
<option value="notequal">Is Not Equal To</option>
<option value="contains">Contains Text</option>
<option value="lessthan">Less Than</option>
<option value="greaterthan">Greater Than</option>
</select>
</OperatorTemplate>
</QueryBuilderTemplates>Custom Value Template
<QueryBuilderTemplates>
<ValueTemplate>
@if (context.Field == "Status") {
<select style="padding: 5px; width: 100%; border: 1px solid #ddd;">
<option value="Pending">Pending</option>
<option value="Processing">Processing</option>
<option value="Completed">Completed</option>
<option value="Cancelled">Cancelled</option>
</select>
} else if (context.Type == "Number") {
<input type="number"
style="padding: 5px; width: 100%; border: 1px solid #ddd;"
placeholder="Enter value" />
} else {
<input type="text"
style="padding: 5px; width: 100%; border: 1px solid #ddd;"
placeholder="Enter value" />
}
</ValueTemplate>
</QueryBuilderTemplates>Dark Mode Support
Implement dark mode styling:
@media (prefers-color-scheme: dark) {
.e-querybuilder {
background-color: #1e1e1e;
color: #e0e0e0;
}
.e-rule {
background-color: #2d2d2d;
border: 1px solid #444;
}
.e-group {
background-color: #252525;
border-left-color: #4a9eff;
}
.e-querybuilder input,
.e-querybuilder select {
background-color: #2d2d2d;
color: #e0e0e0;
border-color: #444;
}
}Responsive Design
Mobile-Optimized Styling
/* Tablet devices */
@media (max-width: 768px) {
.e-querybuilder {
padding: 10px;
}
.e-rule,
.e-group {
padding: 8px;
margin-bottom: 8px;
}
.e-querybuilder button {
font-size: 14px;
padding: 8px;
}
}
/* Mobile devices */
@media (max-width: 480px) {
.e-querybuilder {
padding: 8px;
}
.e-field-select,
.e-operator-select,
.e-value-field input {
width: 100% !important;
margin-bottom: 8px;
font-size: 16px;
}
.e-rule {
display: grid;
grid-template-columns: 1fr;
gap: 8px;
}
.e-querybuilder button {
width: 100%;
margin-top: 5px;
}
}Custom Operator Definitions
Define custom operators and their formatting:
@code {
private List<OperatorModel> CustomOperators = new() {
new() { Text = "Is Equal To", Value = "equal" },
new() { Text = "Is Greater Than", Value = "greaterthan" },
new() { Text = "Is Between", Value = "between" },
new() { Text = "In List", Value = "in" }
};
public class OperatorModel {
public string Text { get; set; }
public string Value { get; set; }
}
}Accessibility Styling
Focus States
/* Keyboard navigation focus */
.e-querybuilder input:focus,
.e-querybuilder select:focus,
.e-querybuilder button:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
/* High contrast mode */
@media (prefers-contrast: more) {
.e-querybuilder {
border: 2px solid #000;
}
.e-querybuilder button {
border: 2px solid #000;
}
}Next Steps
- Advanced Features - Import/export and state persistence
- Localization - Accessibility best practices
- Troubleshooting - Styling issues
Data Binding & Filtered Results
This guide explains how to bind data to the Query Builder, extract filtered results, and apply queries dynamically.
Basic Data Binding
Bind Data Source
@using Syncfusion.Blazor.QueryBuilder
<SfQueryBuilder TValue="Product" DataSource="@Products">
<QueryBuilderColumns>
<QueryBuilderColumn Field="ProductID" Label="ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Name" Label="Product Name" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Price" Label="Price" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Category" Label="Category" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private List<Product> Products = new() {
new() { ProductID = 1, Name = "Laptop", Price = 999.99m, Category = "Electronics" },
new() { ProductID = 2, Name = "Mouse", Price = 29.99m, Category = "Electronics" },
new() { ProductID = 3, Name = "Desk", Price = 299.99m, Category = "Furniture" }
};
public class Product {
public int ProductID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
}Update Data Dynamically
@code {
private async Task LoadProductsFromApi() {
// Simulate API call
Products = await FetchProductsFromServer();
StateHasChanged(); // Notify UI of changes
}
private async Task<List<Product>> FetchProductsFromServer() {
// Replace with actual API call
await Task.Delay(500);
return new() {
new() { ProductID = 1, Name = "Laptop", Price = 999.99m, Category = "Electronics" },
new() { ProductID = 2, Name = "Monitor", Price = 299.99m, Category = "Electronics" }
};
}
}Extracting Filtered Results
Get Query Rules
Retrieve the current query structure as a rule model:
<SfButton OnClick="GetFilteredQuery">Get Query</SfButton>
<SfQueryBuilder TValue="Product" @ref="QueryBuilder" DataSource="@Products">
<QueryBuilderColumns>
<QueryBuilderColumn Field="Category" Label="Category" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Price" Label="Price" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private SfQueryBuilder<Product> QueryBuilder;
private void GetFilteredQuery() {
// Get the current query rules
var rules = QueryBuilder.GetRules();
Console.WriteLine($"Query: {JsonConvert.SerializeObject(rules)}");
}
}Filter Data Using Query
Apply the query to filter your data collection:
<SfButton OnClick="ApplyFilter">Apply Filter</SfButton>
<SfButton OnClick="ShowResults">Show Results</SfButton>
<h4>Filtered Results</h4>
@if (FilteredProducts != null && FilteredProducts.Count > 0) {
<ul>
@foreach (var product in FilteredProducts) {
<li>@product.Name - $@product.Price</li>
}
</ul>
}
@code {
private SfQueryBuilder<Product> QueryBuilder;
private List<Product> FilteredProducts;
private void ApplyFilter() {
var rules = QueryBuilder.GetRules();
FilteredProducts = ApplyQueryToData(Products, rules);
StateHasChanged();
}
private List<Product> ApplyQueryToData(List<Product> data, RuleModel rules) {
// Simple filtering logic (customize based on your query structure)
var filtered = data;
if (rules?.Rules != null) {
foreach (var rule in rules.Rules) {
if (rule.Field == "Category" && rule.Operator == "equal") {
filtered = filtered.Where(p => p.Category == rule.Value.ToString()).ToList();
} else if (rule.Field == "Price" && rule.Operator == "lessthan") {
var price = decimal.Parse(rule.Value.ToString());
filtered = filtered.Where(p => p.Price < price).ToList();
}
}
}
return filtered;
}
}Complex Data Filtering
Multi-Condition Filtering
@code {
private List<Product> FilterWithComplexLogic(List<Product> data, RuleModel rules) {
return data.Where(product => EvaluateRule(product, rules)).ToList();
}
private bool EvaluateRule(Product product, RuleModel rule) {
// Handle nested groups
if (rule.Rules != null && rule.Rules.Count > 0) {
if (rule.Condition == "and") {
return rule.Rules.All(r => EvaluateRule(product, r));
} else if (rule.Condition == "or") {
return rule.Rules.Any(r => EvaluateRule(product, r));
}
}
// Evaluate single rule
return EvaluateSingleRule(product, rule);
}
private bool EvaluateSingleRule(Product product, RuleModel rule) {
var fieldValue = GetFieldValue(product, rule.Field);
return rule.Operator switch {
"equal" => fieldValue.Equals(rule.Value),
"notequal" => !fieldValue.Equals(rule.Value),
"lessthan" => CompareNumeric(fieldValue, rule.Value) < 0,
"greaterthan" => CompareNumeric(fieldValue, rule.Value) > 0,
"contains" => fieldValue.ToString().Contains(rule.Value.ToString()),
"notcontains" => !fieldValue.ToString().Contains(rule.Value.ToString()),
"startswith" => fieldValue.ToString().StartsWith(rule.Value.ToString()),
"endswith" => fieldValue.ToString().EndsWith(rule.Value.ToString()),
_ => false
};
}
private object GetFieldValue(Product product, string field) {
return field switch {
"ProductID" => product.ProductID,
"Name" => product.Name,
"Price" => product.Price,
"Category" => product.Category,
_ => null
};
}
private int CompareNumeric(object a, object b) {
if (decimal.TryParse(a.ToString(), out var numA) &&
decimal.TryParse(b.ToString(), out var numB)) {
return numA.CompareTo(numB);
}
return 0;
}
}Event-Driven Filtering
Real-Time Filter Updates
<SfQueryBuilder TValue="Order" @ref="QueryBuilder" DataSource="@Orders">
<QueryBuilderEvents TValue="Order" RuleChanged="OnQueryChanged"></QueryBuilderEvents>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Amount" Label="Amount" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
<h4>Results: @FilteredOrders?.Count ?? 0 orders</h4>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private List<Order> FilteredOrders;
private void OnQueryChanged(RuleChangeEventArgs args) {
// Automatically apply filter when query changes
var rules = QueryBuilder.GetRules();
FilteredOrders = ApplyQueryToOrders(Orders, rules);
StateHasChanged();
}
private List<Order> ApplyQueryToOrders(List<Order> data, RuleModel rules) {
// Apply filtering logic
return data; // Simplified
}
}Working with Filtered Results
Display and Export
<SfButton OnClick="ExportFiltered">Export Results</SfButton>
@if (FilteredResults != null) {
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@foreach (var item in FilteredResults) {
<tr>
<td>@item.ProductID</td>
<td>@item.Name</td>
<td>@item.Price</td>
</tr>
}
</tbody>
</table>
}
@code {
private List<Product> FilteredResults;
private async Task ExportFiltered() {
var json = JsonConvert.SerializeObject(FilteredResults);
var bytes = Encoding.UTF8.GetBytes(json);
// Trigger download
await JS.InvokeVoidAsync("downloadFile",
"filtered-results.json",
Convert.ToBase64String(bytes));
}
@inject IJSRuntime JS;
}Count & Statistics
@code {
private void ShowStatistics(List<Product> filtered) {
var totalPrice = filtered.Sum(p => p.Price);
var avgPrice = filtered.Average(p => p.Price);
var maxPrice = filtered.Max(p => p.Price);
Console.WriteLine($"Total items: {filtered.Count}");
Console.WriteLine($"Total value: ${totalPrice:F2}");
Console.WriteLine($"Average price: ${avgPrice:F2}");
Console.WriteLine($"Highest price: ${maxPrice:F2}");
}
}Performance Considerations
Virtual Scrolling for Large Datasets
<SfQueryBuilder TValue="Order" DataSource="@Orders" EnableVirtualization="true">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>Pagination
@code {
private int PageSize = 50;
private int CurrentPage = 1;
private List<Product> GetPagedResults(List<Product> filtered) {
return filtered
.Skip((CurrentPage - 1) * PageSize)
.Take(PageSize)
.ToList();
}
}Next Steps
- Events & Callbacks - Handle data binding events
- Advanced Features - Import/export and persistence
- Troubleshooting - Performance and data issues
Events & Callbacks
This guide covers all Query Builder events and how to use them to react to user interactions and component lifecycle changes.
Table of Contents
Component Lifecycle Events
Created Event
Fires when the Query Builder component has been initialized and is ready:
@using Syncfusion.Blazor.QueryBuilder
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderEvents TValue="Order" Created="OnQueryBuilderCreated"></QueryBuilderEvents>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private void OnQueryBuilderCreated() {
Console.WriteLine("Query Builder initialized and ready!");
// Initialize custom logic, set default values, etc.
}
}Destroyed Event
Fires when the Query Builder component is being disposed:
@code {
private void OnQueryBuilderDestroyed() {
Console.WriteLine("Query Builder destroyed, cleaning up resources...");
// Clean up subscriptions, timers, etc.
}
}DataBound Event
Fires after the data source is populated and bound to the Query Builder:
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderEvents TValue="Order" DataBound="OnDataBound"></QueryBuilderEvents>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private void OnDataBound() {
Console.WriteLine("Data bound to Query Builder");
// Apply initial filters, update UI
}
}Rule Change Events
RuleChanged Event
Fires whenever a rule or group is added, removed, or modified:
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderEvents TValue="Order" RuleChanged="OnRuleChanged"></QueryBuilderEvents>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private void OnRuleChanged(RuleChangeEventArgs args) {
// Action: "add", "remove", or "update"
Console.WriteLine($"Action: {args.Action}");
// Rule/Group ID
Console.WriteLine($"ID: {args.RuleID}");
// Get the modified rule
if (args.Rule != null) {
Console.WriteLine($"Field: {args.Rule.Field}");
Console.WriteLine($"Operator: {args.Rule.Operator}");
Console.WriteLine($"Value: {args.Rule.Value}");
}
}
}RuleChangeEventArgs properties:
Action- "add", "remove", or "update"RuleID- ID of the affected ruleRule- The RuleModel that was changed
Changed Event
Fires after a condition (AND/OR), field, operator, or value change is applied:
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderEvents TValue="Order" Changed="OnChanged"></QueryBuilderEvents>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private void OnChanged(ChangeEventArgs args) {
Console.WriteLine("Query has changed");
Console.WriteLine($"Previous value: {args.PreviousValue}");
Console.WriteLine($"Value: {args.Value}");
// Update filtered results
ApplyCurrentFilter();
}
private void ApplyCurrentFilter() {
// Re-apply filter based on updated query
}
}Value Change Events
OnValueChange Event
Fires BEFORE a condition, field, operator, or value is changed. Use this to validate or react before committing:
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderEvents TValue="Order" OnValueChange="OnValueChanging"></QueryBuilderEvents>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Amount" Label="Amount" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private void OnValueChanging(ChangeEventArgs args) {
// Validate the new value
if (args.Value is int newValue && newValue < 0) {
Console.WriteLine("Invalid: Cannot use negative values");
args.Cancel = true; // Prevent the change
}
}
}Use cases for OnValueChange:
- Validate input before committing
- Prevent invalid combinations
- Show confirmation dialogs
- Log user actions
Event Patterns
Multiple Event Handlers
Handle different events in a single component:
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderEvents TValue="Order"
Created="OnCreated"
DataBound="OnDataBound"
RuleChanged="OnRuleChanged"
Changed="OnChanged">
</QueryBuilderEvents>
<QueryBuilderColumns>
<!-- columns -->
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private void OnCreated() {
Console.WriteLine("1. Component created");
}
private void OnDataBound() {
Console.WriteLine("2. Data bound");
}
private void OnRuleChanged(RuleChangeEventArgs args) {
Console.WriteLine($"3. Rule {args.Action}: {args.RuleID}");
}
private void OnChanged(ChangeEventArgs args) {
Console.WriteLine($"4. Changed: {args.Value}");
}
}Event Chaining
@code {
private void OnRuleChanged(RuleChangeEventArgs args) {
// When rule changes, trigger multiple actions
OnRuleChanged_UpdateUI(args);
OnRuleChanged_ValidateQuery(args);
OnRuleChanged_PersistToDatabase(args);
}
private void OnRuleChanged_UpdateUI(RuleChangeEventArgs args) {
Console.WriteLine("Updating UI...");
}
private void OnRuleChanged_ValidateQuery(RuleChangeEventArgs args) {
Console.WriteLine("Validating query...");
}
private void OnRuleChanged_PersistToDatabase(RuleChangeEventArgs args) {
Console.WriteLine("Saving to database...");
}
}Async Callbacks
Async Changed Handler
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderEvents TValue="Order" Changed="OnChangedAsync"></QueryBuilderEvents>
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private async Task OnChangedAsync(ChangeEventArgs args) {
// Perform async operations (API calls, database queries, etc.)
var filteredData = await FetchFilteredDataAsync();
FilteredOrders = filteredData;
StateHasChanged();
}
private async Task<List<Order>> FetchFilteredDataAsync() {
// Simulate API call
await Task.Delay(500);
return new List<Order>();
}
private List<Order> FilteredOrders;
}Debounced Event Handler
@code {
private Timer DebounceTimer;
private void OnChangedDebounced(ChangeEventArgs args) {
// Cancel previous timer
DebounceTimer?.Dispose();
// Schedule action after 500ms delay
DebounceTimer = new Timer(_ => {
OnChangedDebounced_Execute();
}, null, 500, Timeout.Infinite);
}
private void OnChangedDebounced_Execute() {
Console.WriteLine("Executing debounced action...");
// Perform expensive operation (API call, filtering, etc.)
}
public void Dispose() {
DebounceTimer?.Dispose();
}
}Real-World Example: Live Search Filter
@using Syncfusion.Blazor.QueryBuilder
<SfQueryBuilder TValue="Product" DataSource="@Products">
<QueryBuilderEvents TValue="Product"
Created="OnCreated"
Changed="OnChanged">
</QueryBuilderEvents>
<QueryBuilderColumns>
<QueryBuilderColumn Field="Category" Label="Category" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Price" Label="Price" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="InStock" Label="In Stock" Type="ColumnType.Boolean"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
<h4>Results: @FilteredResults?.Count ?? 0 products</h4>
<ul>
@foreach (var item in FilteredResults ?? new()) {
<li>@item.Name - $@item.Price</li>
}
</ul>
@code {
private SfQueryBuilder<Product> QueryBuilder;
private List<Product> Products;
private List<Product> FilteredResults;
private bool IsLoading = false;
protected override void OnInitialized() {
Products = GetAllProducts();
FilteredResults = Products;
}
private void OnCreated() {
Console.WriteLine("Query Builder ready");
}
private async Task OnChanged(ChangeEventArgs args) {
IsLoading = true;
StateHasChanged();
// Simulate processing
await Task.Delay(300);
// Apply filter
FilteredResults = FilterProducts();
IsLoading = false;
StateHasChanged();
}
private List<Product> FilterProducts() {
// Implementation of filtering logic
return Products;
}
private List<Product> GetAllProducts() {
return new() {
new() { Name = "Laptop", Price = 999, InStock = true, Category = "Electronics" },
new() { Name = "Mouse", Price = 29, InStock = true, Category = "Electronics" },
new() { Name = "Desk", Price = 299, InStock = false, Category = "Furniture" }
};
}
public class Product {
public string Name { get; set; }
public decimal Price { get; set; }
public bool InStock { get; set; }
public string Category { get; set; }
}
}Next Steps
- Data Binding - Apply filtered queries to data
- Advanced Features - Import/export queries
- Troubleshooting - Common event issues
Filtering, Rules & Groups
This guide covers the core functionality of adding, deleting, and managing rules and groups programmatically in the Query Builder.
Table of Contents
- Adding Rules
- Deleting Rules
- Adding Groups
- Deleting Groups
- Operators & Conditions
- Complex Nested Patterns
- User Interaction
- Validation & Constraints
Adding Rules
Basic Rule Addition
Add a rule programmatically using the AddRule method:
<SfButton OnClick="AddRule">Add Rule</SfButton>
<SfQueryBuilder TValue="Order" @ref="QueryBuilder">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="Status" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private SfQueryBuilder<Order> QueryBuilder;
private void AddRule() {
var rule = new RuleModel {
Label = "Order ID",
Field = "OrderID",
Type = "Number",
Operator = "greater",
Value = 5000
};
QueryBuilder.AddRule(rule, "group0"); // Add to root group
}
public class Order {
public int OrderID { get; set; }
public string Status { get; set; }
}
}Parameters:
rule- RuleModel with Field, Operator, ValuegroupId- Parent group ID (e.g., "group0" for root)
Multiple Rules
private void AddMultipleRules() {
var rule1 = new RuleModel {
Field = "OrderID",
Type = "Number",
Operator = "greater",
Value = 1000
};
var rule2 = new RuleModel {
Field = "Status",
Type = "String",
Operator = "equal",
Value = "Completed"
};
QueryBuilder.AddRule(rule1, "group0");
QueryBuilder.AddRule(rule2, "group0");
}Deleting Rules
Remove a rule by its ID:
<SfButton OnClick="DeleteRule">Delete First Rule</SfButton>
@code {
private void DeleteRule() {
// Delete rule with ID "rule0"
QueryBuilder.DeleteRule("rule0");
}
}Adding Groups
Groups combine multiple rules with AND/OR logic:
<SfButton OnClick="AddGroup">Add Condition Group</SfButton>
@code {
private void AddGroup() {
var group = new RuleModel {
Condition = "or", // AND or OR
Rules = new List<RuleModel> {
new RuleModel {
Field = "Category",
Type = "String",
Operator = "equal",
Value = "Electronics"
},
new RuleModel {
Field = "Price",
Type = "Number",
Operator = "lessthan",
Value = 500
}
}
};
QueryBuilder.AddGroup(group, "group0");
}
}Deleting Groups
Remove an entire group:
<SfButton OnClick="DeleteGroup">Delete Group</SfButton>
@code {
private void DeleteGroup() {
// Delete group with ID "group1"
QueryBuilder.DeleteGroup("group1");
}
}Operators & Conditions
String Field Filtering
@code {
private void StringFiltering() {
// Contains pattern
var rule1 = new RuleModel {
Field = "CustomerName",
Type = "String",
Operator = "contains",
Value = "Corp"
};
// Starts with
var rule2 = new RuleModel {
Field = "Email",
Type = "String",
Operator = "startswith",
Value = "admin"
};
// In list
var rule3 = new RuleModel {
Field = "Status",
Type = "String",
Operator = "in",
Value = new[] { "Active", "Pending" }
};
QueryBuilder.AddRule(rule1, "group0");
QueryBuilder.AddRule(rule2, "group0");
QueryBuilder.AddRule(rule3, "group0");
}
}Number Field Filtering
@code {
private void NumberFiltering() {
// Greater than
var rule1 = new RuleModel {
Field = "Salary",
Type = "Number",
Operator = "greaterthan",
Value = 50000
};
// Between range
var rule2 = new RuleModel {
Field = "Age",
Type = "Number",
Operator = "between",
Value = new[] { 25, 65 }
};
// Not equal
var rule3 = new RuleModel {
Field = "Quantity",
Type = "Number",
Operator = "notequal",
Value = 0
};
QueryBuilder.AddRule(rule1, "group0");
QueryBuilder.AddRule(rule2, "group0");
QueryBuilder.AddRule(rule3, "group0");
}
}Date Field Filtering
@code {
private void DateFiltering() {
// After date
var rule1 = new RuleModel {
Field = "OrderDate",
Type = "Date",
Operator = "greaterthan",
Value = new DateTime(2024, 1, 1)
};
// Between date range
var rule2 = new RuleModel {
Field = "HireDate",
Type = "Date",
Operator = "between",
Value = new[] {
new DateTime(2020, 1, 1),
new DateTime(2024, 12, 31)
}
};
QueryBuilder.AddRule(rule1, "group0");
QueryBuilder.AddRule(rule2, "group0");
}
}Complex Nested Patterns
Three-Level Nesting
@code {
private void ComplexNesting() {
// Level 1: Root group (AND)
var level1Group = new RuleModel {
Condition = "and",
Rules = new List<RuleModel> {
// Level 2: Nested group (OR)
new RuleModel {
Condition = "or",
Rules = new List<RuleModel> {
new RuleModel {
Field = "Category",
Type = "String",
Operator = "equal",
Value = "Electronics"
},
new RuleModel {
Field = "Category",
Type = "String",
Operator = "equal",
Value = "Software"
}
}
},
// Level 2: Another rule
new RuleModel {
Field = "Price",
Type = "Number",
Operator = "lessthan",
Value = 1000
}
}
};
QueryBuilder.AddGroup(level1Group, "group0");
}
}Business Logic Example
private void BusinessLogicFilter() {
// Find orders matching:
// (Status = "Pending" OR Status = "Processing")
// AND (Amount > 500 OR CreatedDate > 7 days ago)
var statusGroup = new RuleModel {
Condition = "or",
Rules = new List<RuleModel> {
new RuleModel {
Field = "Status",
Type = "String",
Operator = "equal",
Value = "Pending"
},
new RuleModel {
Field = "Status",
Type = "String",
Operator = "equal",
Value = "Processing"
}
}
};
var amountGroup = new RuleModel {
Condition = "or",
Rules = new List<RuleModel> {
new RuleModel {
Field = "Amount",
Type = "Number",
Operator = "greaterthan",
Value = 500
},
new RuleModel {
Field = "CreatedDate",
Type = "Date",
Operator = "greaterthan",
Value = DateTime.Now.AddDays(-7)
}
}
};
QueryBuilder.AddGroup(statusGroup, "group0");
QueryBuilder.AddGroup(amountGroup, "group0");
}User Interaction
ShowButtons Configuration
Control which action buttons display in the UI:
<SfQueryBuilder TValue="Order">
<QueryBuilderShowButtons
RuleDelete="true"
GroupInsert="true"
GroupDelete="true">
</QueryBuilderShowButtons>
<QueryBuilderColumns>
<!-- columns -->
</QueryBuilderColumns>
</SfQueryBuilder>Available buttons:
RuleDelete- Delete rule buttonGroupDelete- Delete group buttonGroupInsert- Add group button
Capture User Actions
<SfQueryBuilder TValue="Order" DataSource="@Orders">
<QueryBuilderEvents TValue="Order" RuleChanged="OnRuleChanged"></QueryBuilderEvents>
<QueryBuilderColumns>
<!-- columns -->
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private void OnRuleChanged(RuleChangeEventArgs args) {
Console.WriteLine($"Rule action: {args.Action}"); // add, remove, update
Console.WriteLine($"Rule ID: {args.RuleID}");
}
}Validation & Constraints
Prevent Invalid Rules
@code {
private void ValidateAndAdd() {
var rule = new RuleModel {
Field = "Salary",
Type = "Number",
Operator = "equal",
Value = -1000 // Invalid: negative salary
};
// Validate before adding
if (ValidateRule(rule)) {
QueryBuilder.AddRule(rule, "group0");
}
}
private bool ValidateRule(RuleModel rule) {
// Custom validation logic
if (rule.Field == "Salary" && rule.Type == "Number") {
if (rule.Value is int val && val < 0) {
Console.WriteLine("Invalid: Salary cannot be negative");
return false;
}
}
return true;
}
}Next Steps
- Data Binding - Apply filtered queries to data
- Events & Callbacks - Handle rule changes
- Advanced Features - Import/export and state persistence
Getting Started with Blazor Query Builder
This guide walks through installing and setting up the Syncfusion Blazor Query Builder component in both Blazor WebAssembly and Blazor Server applications.
Prerequisites
- .NET 6.0 or later
- Visual Studio 2022 or Visual Studio Code
- Basic knowledge of Blazor and C#
Installation
Step 1: Create a Blazor Project
Via Visual Studio
1. Open Visual Studio 2022 2. Create New Project → Blazor WebAssembly App or Blazor Server App 3. Configure project name and location 4. Select target framework (.NET 8.0 or later)
Via Command Line
# For Blazor WebAssembly
dotnet new blazorwasm -n QueryBuilderApp
cd QueryBuilderApp
# For Blazor Server
dotnet new blazorserver -n QueryBuilderApp
cd QueryBuilderAppStep 2: Install NuGet Packages
Install the Query Builder and Themes packages:
dotnet add package Syncfusion.Blazor.QueryBuilder
dotnet add package Syncfusion.Blazor.Themes
dotnet restoreOr via Package Manager Console:
Install-Package Syncfusion.Blazor.QueryBuilder
Install-Package Syncfusion.Blazor.ThemesStep 3: Register Syncfusion Services
In Program.cs, add the Syncfusion services:
using Syncfusion.Blazor;
var builder = WebApplication.CreateBuilder(args);
// For Blazor WebAssembly
builder.Services.AddScoped(sp => new HttpClient {
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
// For Blazor Server
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
// Register Syncfusion services
builder.Services.AddSyncfusionBlazor();
var app = builder.Build();
// ... rest of configurationStep 4: Add Syncfusion Theme
In your host page (_Host.cshtml for Blazor Server or index.html for Blazor WebAssembly), add the theme CSS in the <head> section:
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Query Builder App</title>
<base href="~/" />
<!-- Syncfusion Theme -->
<link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/bootstrap5.css">
<link rel="stylesheet" href="css/app.css" />
</head>Available themes:
bootstrap5.css- Bootstrap 5 themematerial3.css- Material Design 3 themefluent2.css- Microsoft Fluent 2 themetailwindcss.css- Tailwind CSS theme
Step 5: Create Your First Query Builder
Create or edit a component (e.g., Components/QueryBuilderDemo.razor):
@page "/query-builder"
@using Syncfusion.Blazor.QueryBuilder
<h3>Query Builder Demo</h3>
<SfQueryBuilder TValue="Employee" DataSource="@EmployeeData">
<QueryBuilderColumns>
<QueryBuilderColumn Field="EmployeeID" Label="Employee ID" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="FirstName" Label="First Name" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="Department" Label="Department" Type="ColumnType.String"></QueryBuilderColumn>
<QueryBuilderColumn Field="HireDate" Label="Hire Date" Type="ColumnType.Date" Format="MM/dd/yyyy"></QueryBuilderColumn>
<QueryBuilderColumn Field="IsActive" Label="Active" Type="ColumnType.Boolean"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private List<Employee> EmployeeData = new();
protected override void OnInitialized() {
EmployeeData = new() {
new() { EmployeeID = 1, FirstName = "John", Department = "Engineering", HireDate = new(2020, 3, 15), IsActive = true },
new() { EmployeeID = 2, FirstName = "Jane", Department = "Sales", HireDate = new(2021, 5, 20), IsActive = true },
new() { EmployeeID = 3, FirstName = "Bob", Department = "HR", HireDate = new(2019, 1, 10), IsActive = false }
};
}
public class Employee {
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string Department { get; set; }
public DateTime HireDate { get; set; }
public bool IsActive { get; set; }
}
}Step 6: Run the Application
dotnet runNavigate to your component's route (e.g., /query-builder) to see the Query Builder in action.
Verifying Installation
You should see a Query Builder with:
- Column dropdown for selecting fields
- Operator dropdown for comparison types
- Input field for entering filter values
- Add Rule and Add Group buttons
- Delete buttons for removing rules/groups
If the Query Builder doesn't render: 1. Check browser console (F12) for JavaScript errors 2. Verify CSS link is correct in the host page 3. Ensure AddSyncfusionBlazor() is called in Program.cs 4. Check that NuGet packages are properly installed
Next Steps
- Basic Setup - Configure columns and default rules
- Filtering & Rules - Add/delete rules programmatically
- Events & Callbacks - Handle query changes
- Customization - Style and template the UI
Localization & Accessibility
This guide covers multi-language support, RTL (Right-to-Left) support, and accessibility best practices for the Query Builder component.
Localization
Configure Locale
Set the locale in your Blazor application:
// In Program.cs
using Syncfusion.Blazor;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSyncfusionBlazor();
builder.Services.Configure<SyncfusionBlazorSettings>(options =>
{
options.Locale = "fr-FR"; // Set to French
});
var app = builder.Build();Supported Locales
Query Builder supports multiple locales:
en-US- English (United States, default)fr-FR- Frenchde-DE- Germanes-ES- Spanishja-JP- Japanesezh-CN- Chinese (Simplified)zh-TW- Chinese (Traditional)ar-AR- Arabicru-RU- Russian- And many more...
Custom Localization
Define custom translated strings:
// In Program.cs
var localization = new SfLocalization();
localization.SetLocale(new Dictionary<string, object>
{
{ "en-US", new Dictionary<string, string>
{
{ "AddRule", "Add Rule" },
{ "AddGroup", "Add Group" },
{ "DeleteGroup", "Delete Group" }
}},
{ "es-ES", new Dictionary<string, string>
{
{ "AddRule", "Agregar Regla" },
{ "AddGroup", "Agregar Grupo" },
{ "DeleteGroup", "Eliminar Grupo" }
}}
});Right-to-Left (RTL) Support
Enable RTL Mode
<SfQueryBuilder TValue="Order" EnableRtl="true">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="معرّف الطلب" Type="ColumnType.Number"></QueryBuilderColumn>
<QueryBuilderColumn Field="Status" Label="الحالة" Type="ColumnType.String"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>RTL with Locale
<SfQueryBuilder TValue="Order" EnableRtl="true">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="معرّف الطلب" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>RTL CSS
/* Automatic RTL adjustments for Arabic/Hebrew */
[dir="rtl"] .e-querybuilder {
direction: rtl;
text-align: right;
}
[dir="rtl"] .e-rule {
text-align: right;
padding-right: 15px;
padding-left: 5px;
}
[dir="rtl"] .e-querybuilder button {
margin-left: auto;
margin-right: 0;
}Accessibility Best Practices
WCAG Compliance
Query Builder follows WCAG 2.1 Level AA accessibility standards:
- Keyboard navigation
- Screen reader support
- Color contrast requirements
- Focus indicators
- ARIA labels
Keyboard Navigation
Enable full keyboard navigation:
<SfQueryBuilder TValue="Order">
<QueryBuilderColumns>
<QueryBuilderColumn Field="OrderID" Label="Order ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
<!-- Navigation keys:
Tab - Move to next element
Shift+Tab - Move to previous element
Enter - Activate button or open dropdown
Space - Activate button
Escape - Close dropdown
Arrow keys - Select options in dropdown
-->ARIA Attributes
Query Builder automatically provides ARIA attributes:
<!-- Generated ARIA attributes -->
<div class="e-querybuilder" role="region" aria-label="Query Builder">
<div class="e-rule" role="group" aria-label="Rule">
<select aria-label="Select Field">
<!-- options -->
</select>
</div>
</div>Color Contrast
Ensure sufficient contrast for readability:
/* High contrast colors for accessibility */
.e-querybuilder {
color: #212529; /* Dark gray on light background */
background-color: #ffffff;
}
.e-querybuilder .e-rule {
border: 2px solid #495057; /* Visible border */
background-color: #ffffff;
}
/* Focus indicators */
.e-querybuilder input:focus,
.e-querybuilder select:focus,
.e-querybuilder button:focus {
outline: 3px solid #0d6efd; /* Blue outline */
outline-offset: 2px;
}Screen Reader Support
Make Query Builder screen-reader friendly:
<SfQueryBuilder TValue="Order" AccessibilityLabel="Filter Orders by Conditions">
<QueryBuilderColumns>
<!-- Provide descriptive labels -->
<QueryBuilderColumn
Field="OrderID"
Label="Order ID"
Type="ColumnType.Number"
AriaLabel="Select Order ID to filter">
</QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>Label All Fields
<!-- ✓ Good: Clear labels -->
<label for="query-field">
<span>Filter Field</span>
<select id="query-field" aria-label="Select field to filter">
<!-- options -->
</select>
</label>
<!-- ✗ Bad: Missing labels -->
<select>
<!-- options -->
</select>Error Messages for Accessibility
@code {
private string ErrorMessage = "";
private async Task ValidateQuery() {
try {
var rules = QueryBuilder.GetRules();
if (rules == null || rules.Rules.Count == 0) {
ErrorMessage = "Error: Please add at least one condition";
// Announce to screen readers
await JS.InvokeVoidAsync("announce", ErrorMessage);
}
} catch (Exception ex) {
ErrorMessage = $"Error: {ex.Message}";
await JS.InvokeVoidAsync("announce", ErrorMessage);
}
}
}Accessibility Checklist
- [ ] Keyboard navigation works for all controls
- [ ] Focus indicators are visible
- [ ] Color contrast meets WCAG AA standards (4.5:1 for text)
- [ ] All inputs have associated labels
- [ ] Error messages are clear and associated with fields
- [ ] Screen readers announce all important information
- [ ] Component works with browser zoom (up to 200%)
- [ ] No content relies solely on color
- [ ] Animations can be disabled (prefers-reduced-motion)
Reduced Motion Support
Respect user's motion preferences:
/* Disable animations for users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
.e-querybuilder * {
animation: none !important;
transition: none !important;
}
}Next Steps
- Advanced Features - Import/export and persistence
- Troubleshooting - Accessibility issues
- Customization - Styling accessibility
Troubleshooting
This guide helps you diagnose and resolve common Query Builder issues.
Installation & Setup Issues
Error: "Query Builder component not rendering"
Symptoms: Component appears blank or missing from UI
Solutions: 1. Verify CSS theme is linked:
<link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/bootstrap5.css">2. Check Syncfusion services are registered in Program.cs:
builder.Services.AddSyncfusionBlazor();3. Verify NuGet packages are installed:
dotnet add package Syncfusion.Blazor.QueryBuilder4. Check browser console (F12) for JavaScript errors
Error: "Cannot find Syncfusion namespace"
Solutions: 1. Add using directive:
@using Syncfusion.Blazor.QueryBuilder2. Verify project file includes theme package:
<PackageReference Include="Syncfusion.Blazor.Themes" Version="*" />Data Binding Issues
Columns not appearing in dropdown
Cause: DataSource not bound or columns not configured
Solution:
<!-- Ensure both DataSource and Columns are set -->
<SfQueryBuilder TValue="Employee" DataSource="@Employees">
<QueryBuilderColumns>
<QueryBuilderColumn Field="EmployeeID" Label="ID" Type="ColumnType.Number"></QueryBuilderColumn>
</QueryBuilderColumns>
</SfQueryBuilder>
@code {
private List<Employee> Employees = new(); // Must not be null
protected override void OnInitialized() {
Employees = GetEmployees(); // Initialize in OnInitialized
}
}DataSource updates don't reflect in Query Builder
Solution: Call StateHasChanged() after updating data:
@code {
private async Task LoadData() {
Employees = await FetchFromApi();
StateHasChanged(); // Notify UI of changes
}
}GetFilteredRecords returns null
Cause: Rules not properly extracted
Solution:
@code {
private async Task GetFiltered() {
try {
var rules = await QueryBuilder.GetRules();
if (rules != null) {
var filtered = ApplyRulesToData(Data, rules);
Console.WriteLine($"Filtered: {filtered.Count} records");
} else {
Console.WriteLine("No rules defined");
}
} catch (Exception ex) {
Console.WriteLine($"Error: {ex.Message}");
}
}
}Event Issues
Events not firing
Symptoms: RuleChanged, Changed events don't trigger
Solutions:
1. Verify event handler is defined:
<QueryBuilderEvents TValue="Order" RuleChanged="OnRuleChanged"></QueryBuilderEvents>2. Check handler method signature:
@code {
private void OnRuleChanged(RuleChangeEventArgs args) {
// Correct signature
}
// ✗ Wrong - missing parameter
// private void OnRuleChanged() { }
}3. Ensure event is triggered by user action (not programmatic changes)
OnValueChange validation not preventing changes
Solution: Set Cancel property on event args:
@code {
private void OnValueChanging(ChangeEventArgs args) {
if (!IsValid(args.Value)) {
args.Cancel = true; // Prevent change
Console.WriteLine("Invalid value, change cancelled");
}
}
}Performance Issues
Query Builder slow with large datasets
Symptoms: Lag when opening dropdown or filtering
Solutions:
1. Enable virtual scrolling:
<SfQueryBuilder TValue="Order" DataSource="@Orders" EnableVirtualization="true">
</SfQueryBuilder>2. Use pagination:
@code {
private int PageSize = 100;
private List<Order> PagedOrders => Orders.Skip(0).Take(PageSize).ToList();
}3. Reduce initial data load:
@code {
protected override async Task OnInitializedAsync() {
// Load only first 1000 records initially
Orders = await FetchOrdersAsync(0, 1000);
}
}Memory leaks in long-running applications
Solution: Implement proper cleanup:
@implements IAsyncDisposable
@code {
private SfQueryBuilder<Order> QueryBuilder;
async ValueTask IAsyncDisposable.DisposeAsync() {
if (QueryBuilder != null) {
await QueryBuilder.DisposeAsync();
}
}
}Styling Issues
Styles not applied to Query Builder
Symptoms: Custom CSS classes not working
Solutions:
1. Verify theme CSS is loaded before custom CSS:
<link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/bootstrap5.css">
<link rel="stylesheet" href="css/custom.css"> <!-- After Syncfusion theme -->2. Check CSS specificity:
/* Increase specificity if needed */
.e-querybuilder .e-rule {
background-color: #fff !important;
}3. Verify correct CSS class names:
/* ✓ Correct -->
.e-querybuilder .e-rule { }
/* ✗ Wrong -->
.query-builder-rule { }Components not visible on mobile
Solution: Add responsive CSS:
@media (max-width: 768px) {
.e-querybuilder {
padding: 10px;
}
.e-rule {
display: grid;
grid-template-columns: 1fr;
}
}Configuration Issues
Column Type not matching operators
Symptoms: Operators don't appear or are incorrect
Solution: Ensure Column Type matches field data type:
<!-- ✓ Correct -->
<QueryBuilderColumn Field="Salary" Label="Salary" Type="ColumnType.Number"></QueryBuilderColumn>
<!-- ✗ Wrong - should be Number -->
<QueryBuilderColumn Field="Salary" Label="Salary" Type="ColumnType.String"></QueryBuilderColumn>Date formats not working
Solution: Specify date format explicitly:
<QueryBuilderColumn
Field="OrderDate"
Label="Order Date"
Type="ColumnType.Date"
Format="MM/dd/yyyy">
</QueryBuilderColumn>Boolean columns showing text instead of checkbox
Solution: Use correct column type:
<!-- ✓ Correct -->
<QueryBuilderColumn Field="IsActive" Label="Active" Type="ColumnType.Boolean"></QueryBuilderColumn>
<!-- ✗ Wrong -->
<QueryBuilderColumn Field="IsActive" Label="Active" Type="ColumnType.String"></QueryBuilderColumn>Validation Issues
Validation rules not enforced
Solution: Implement validation in event handler:
@code {
private void OnValueChanging(ChangeEventArgs args) {
if (args.Field == "Salary" && args.Value is int salary) {
if (salary < 0 || salary > 1000000) {
args.Cancel = true;
Console.WriteLine("Invalid salary range");
}
}
}
}Export/Import Issues
Query export returns null
Symptoms: GetRules() returns null or empty
Solution:
@code {
private async Task ExportSafely() {
var rules = QueryBuilder.GetRules();
if (rules == null) {
Console.WriteLine("No rules to export");
return;
}
if (rules.Rules == null || rules.Rules.Count == 0) {
Console.WriteLine("Rules collection is empty");
return;
}
var json = JsonConvert.SerializeObject(rules);
Console.WriteLine(json);
}
}Import creates invalid rules
Solution: Validate before import:
@code {
private bool ValidateImportedRules(RuleModel rules) {
if (rules?.Rules == null || rules.Rules.Count == 0) {
Console.WriteLine("Invalid rules structure");
return false;
}
foreach (var rule in rules.Rules) {
if (string.IsNullOrEmpty(rule.Field) || string.IsNullOrEmpty(rule.Operator)) {
Console.WriteLine("Rule missing required fields");
return false;
}
}
return true;
}
}Common Workarounds
Refresh Query Builder after data change
@code {
private SfQueryBuilder<Order> QueryBuilder;
private async Task RefreshQueryBuilder() {
// Force refresh
await QueryBuilder.Refresh();
}
}Clear all rules
@code {
private async Task ClearAllRules() {
var rules = QueryBuilder.GetRules();
foreach (var rule in rules.Rules.ToList()) {
if (rule.Rules == null) { // It's a rule, not a group
await QueryBuilder.DeleteRule(rule.Id);
}
}
}
}Log component state for debugging
@code {
private async Task LogComponentState() {
var rules = QueryBuilder.GetRules();
Console.WriteLine("=== Query Builder State ===");
Console.WriteLine($"Rules: {rules.Rules?.Count ?? 0}");
Console.WriteLine($"Condition: {rules.Condition}");
Console.WriteLine(JsonConvert.SerializeObject(rules, Formatting.Indented));
}
}Getting Help
1. Check browser console (F12) for JavaScript errors 2. Review documentation in getting-started.md 3. Enable debug logging in your application 4. Create minimal reproduction with simplified code 5. Contact Syncfusion support with error details and code sample
Next Steps
- Review advanced-features.md for optimization
- Check events-and-callbacks.md for event handling
- See customization-and-styling.md for styling help