Skip to main content

FilterBuilder

FilterBuilder is the core component of the SVAR Svelte Filter package which:

  • Enables users to build complex filter queries across multiple data fields
  • Supports grouping and AND/OR logic to create nested, structured filters
  • Suitable for advanced use cases requiring high granularity and flexibility in data filtering

Initialization

To initialize the FilterBuilder widget, import the component and add it to your markup:

<script>
import { FilterBuilder } from "wx-svelte-filter";
</script>

<FilterBuilder />

This renders an empty FilterBuilder builder. To make it functional, you must pass at least the fields and options properties.

Fields is an array of data fields to filter, each of which contains field id, text label and the desired filter type for it. Options is an object where keys are field ids and values are the options for these fields:

<script>
const fields = [
{ id: "age", label: "Age", type: "number" },
{ id: "country", label: "Country", type: "text" }
];

const options = {
age: [24, 26, 33, 35, 44, 45, 62], // options for the age field
country: ["USA", "China", "Germany"], // options for the country field
};
</script>

<FilterBuilder {fields} {options}/>

How to define filter types for fields

Filters can be of text, number, tuple or date type. Each type supports appropriate operators and is defined for each field as its type:

const fields = [
{ id: "first_name", label: "Name", type: "text" },
{ id: "age", label: "Age", type: "number" },
{ id: "start", label: "Start Date", type: "date" }
];

How to define field options

Options for FilterBuilder widget represent all possible values of its fields. They contain the object with field ids as keys:

  const options = {
age: [24, 26, 33, 35, 44, 45, 62], // options for the age field
country: ["USA", "China", "Germany"], // options for the country field
};

To collect options from the array of data to filter, use the getOptionsMap helper:

<script>
import { getData } from "../data";
import { FilterBuilder, getOptionsMap } from "wx-svelte-filter";

const { fields, data } = getData();
</script>

<FilterBuilder {fields} options={getOptionsMap(data)}/>

To load options dynamically, define them as a function that is called each time a filter for this field is created. It returns either an array of options or a Promise that resolves with this array:

<script>
import { FilterBuilder } from "wx-svelte-filter";

async function provideOptions(fieldId) {
const response = await fetch("server/url/"+fieldId);
let options = await response.json();
// convert string dates, if any
return options;
}
</script>

<FilterBuilder {fields} {value} options={provideOptions} />

How to create a basic filter

A basic filter is a simple group of rules without any nesting. You can define one or more rules and pass them to the value property. Each group contains:

  • rules - an array of filters
  • glue - ("and"|"or") defines how filters are combined in this group

The example below creates a filter for users where age is greater than 25 AND country is USA:

<script>
import { FilterBuilder } from "wx-svelte-filter";

const fields = [
{ id: "age", label: "Age", type: "number" },
{ id: "country", label: "Country", type: "text" }
];

const options = {
age: [24, 26, 33, 35, 44, 45, 62],
country: ["USA", "China", "Germany"],
};

const value = {
glue: "and",
rules: [
{ field: "age", filter: "greater", value: 25 },
{ field: "country", filter: "equal", value: "USA" }
]
};
</script>

<FilterBuilder {fields} {options} {value} />

How to create a filter with a nested structure

To create advanced filter logic with grouped conditions, pass a nested structure to the value property.

The following example defines a filter where:

  • First name contains “John” OR last name contains “Smith”
  • AND Country is USA
  • AND Age is greater than 30
<script>
import { FilterBuilder } from "wx-svelte-filter";

const fields = [
{ id: "first_name", label: "First Name", type: "text" },
{ id: "last_name", label: "Last Name", type: "text" },
{ id: "age", label: "Age", type: "number" },
{ id: "country", label: "Country", type: "text" }
];

const options = {
country: ["USA", "Canada", "Germany"],
age: [24, 26, 33, 35, 44, 45, 62],
first_name: ["John", "Alex", "Daisy"],
last_name: ["Smith", "Brown", "Taylor"]
};

const value = {
glue: "and",
rules: [
{
glue: "or",
rules: [
{ field: "first_name", filter: "contains", value: "John", type: "text" },
{ field: "last_name", filter: "contains", value: "Smith", type: "text" }
]
},
{ field: "country", filter: "equal", value: "USA", type: "text" },
{ field: "age", filter: "greater", value: 30, type: "number" }
]
};
</script>

<FilterBuilder {fields} {options} {value} />

How to filter the data

The FilterBuilder widget provides a visual interface for building filter conditions, but it does not filter your dataset on its own.

To apply filters to an array of data, listen to the onchange event of the FilterBuilder widget (also known as the change action) and use the createArrayFilter helper. This helper returns a filtering function based on the current FilterBuilder structure, which you can use to filter your dataset.

<script>
import { getData } from "../data";
import { FilterBuilder, createArrayFilter } from "wx-svelte-filter";

const { data, value } = getData();
let filteredData = $state(data);

// Filter the data according to filtering rules
function applyFilter(value) {
const filter = createArrayFilter(value);
filteredData = filter(data);
}

// Apply the initial filter state
applyFilter(value);
</script>

<FilterBuilder {value} onchange={({ value }) => applyFilter(value)}/>

How to switch to different display modes

Use the type property to change the visual layout:

<FilterBuilder {fields} {options} type="line" />

Available modes:

  • list – (default) vertical layout with rule nesting
  • line – horizontal layout with rule nesting
  • simple – horizontal layout with AND logic only, no nesting

How to disable rule editing upon adding

You can intercept adding rules and groups by using the api.intercept() method with the add-rule and add-group actions.

The example below shows how to add rules and groups without opening them in the editor immediately.

<script>
import { getData } from "../data";
import { FilterBuilder } from "wx-svelte-filter";

const { fields, options } = getData();

// ev.edit is true by default
function init(api) {
api.intercept("add-rule", ev => {
ev.edit = false;
});

api.intercept("add-group", ev => {
ev.edit = false;
});
});

<FilterBuilder {init} {fields} {options} />

How to track changes in rules

The component supports tracking user interaction through the change action also known as the onchange event. This event fires every time users update, add, or delete a rule, or change the glue logic (AND/OR). It can be used to retrieve the full FilterBuilder value to create a filtering function for your data.

If you want to track specific steps you can use the update-rule action. It fires after users press the "Apply" button. There are also separate actions for adding rules or groups as well as deleting rules.

When you want to preserve user progress without waiting for them to click "Apply", you need to use the change-rule action.This is useful for live updates when you want to react to filter changes in real time.

<script>
import { getData } from "./common/data";
import { FilterBuilder } from "wx-svelte-filter";

const { fields, options } = getData();

const init = (api) => {
api.on("change-rule", ev => {
console.log("Rule is being changed:", ev.id, ev.rule);
});

api.on("update-rule", ev => {
console.log("Rule has been updated:", ev.id, ev.rule);
});

api.on("change", ev => {
console.log("Value has changed to: ", ev.value);
});
};
</script>

<FilterBuilder {init} {fields} {options} />

Working with dates

FilterBuilder works with valid JavaScript Date objects. If your data contains date strings, convert them to Date objects before using them in the value or options properties:

<script>
import { FilterBuilder } from "wx-svelte-filter";

const options = {
start:[ new Date(2024, 0, 14), new Date(2024, 6, 1) ],
end:[ new Date(2024, 7, 15), new Date(2024, 10, 12) ]
};

const fields = [
{ id: "start", label: "Start Date", type: "date" },
{ id: "end", label: "End Date", type: "date"},
];

const value = {
glue: "or",
rules: [
{ field: "start", filter: "greater", value: new Date() },
{ field: "country", filter: "between", value: {
start:new Date(2024, 0, 1),
end: new Date(2024, 11, 31)
}}
]
};
</script>

<FilterBuilder {value} {fields} {options} />

If you want to use only month or year numbers extracted from date fields, apply the getOptionsMap (getOptions for a single field) helpers and pass the predicate ("month" or "year") to the config. Such options are used for "number" or "tuple" filters:

<script>
import { FilterBuilder, getOptionsMap } from "wx-svelte-filter";

const data = [
{ start: new Date(2024, 0, 14), end: new Date(2024, 7, 15) },
{ start: new Date(2024, 6, 1), end: new Date(2024, 10, 12) }
];

const fields = [
{ id: "start", label: "Start Month", type: "number"},
{ id: "end", label: "End Month", type: "number"},
];

const options = getOptionsMap( data, {
{ id: "start", predicate:"month"},
{ id: "end", predicate:"month"},
});
/* output
{ start: [0,7], end:[6,10] }
*/
</script>
<FilterBuilder {fields} {options}>

Date and number formatting

By default, dates are formatted in UI according to the current locale. You can customize how the date is displayed using the format parameter inside fields that can be:

  • string - defines format for "date" filter according to the rules;
  • function - defines format for "date", "number" and "tuple" filters.
const fields = [
// date - change default date format
{
id: "start",
type: "date",
format: "%d/%m/%Y"
},
// number - display formatted values
{
id: "salary",
type: "number",
format: (n) => n.toLocaleString()
},
// tuple - map numbers to human-readable options in a dropdown list
{
id: "month",
type: "number",
format: (v) => {
const months = ["Jan", "Feb", "Mar", "Apr"];
return months[v];
}
}
];

Integration with backend

The FilterBuilder widget can be integrated into applications where filtering takes place on the server side.

  • You should send the value object with structured filter rules to your backend via a POST request
  • The backend should parse these rules, apply them to database query and return the filtered data
  • If you need to load filter options, define them as an async function that takes field id and returns a Promise that resolves with an array with options
  • You need to perform the Date to string (and vice versa) conversions if necessary.

The example below shows how to connect FilterBuilder and SVAR Svelte Grid with backend filtering:

<script>
import { Grid } from "wx-svelte-grid";
import { FilterBuilder } from "wx-svelte-filter";
import { getData } from "../data";

// Initial config
const { fields, value, columns } = getData();
const server = "https://your-backend.com/api/data/persons";

let data = $state([]);

// Initial data load with filters
loadFilteredData(value);

// Function to send filter rules to backend
async function loadFilteredData(value) {
const response = await fetch(server, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(value)
});
data = await response.json();
}

// Function to load options dynamically
async function provideOptions(fieldId) {
const response = await fetch(`${server}/${fieldId}/suggest`);
let options = await response.json();

// Convert strings to Date objects for date fields
const field = fields.find(f => f.id === fieldId);
if (field.type === "date") {
options = options.map(value => new Date(value));
}

return options;
}
</script>

<FilterBuilder
{fields}
{value}
options={provideOptions}
onchange={({ value }) => loadFilteredData(value)}
/>

<Grid {data} {columns} />
info

A ready-made Go backend is available that can convert FilterBuilder values into SQL queries, allowing you to filter data directly on the server side.