A lightweight, flexible URL query parameter management system for Vue applications.
Works seamlessly with Spatie laravel-query-builder and Inertia.js for filtering and sorting data in Laravel applications.
npm install @veebisepad/vue-query-filters
import { useFilters, createFilterFactory } from '@veebisepad/vue-query-filters';
import { watch } from 'vue';
// Create filter factory
const f = createFilterFactory();
// Define your filters
const filters = useFilters(
{
category: f.single(),
brands: f.multiple(),
price: f.range(),
},
{
onApply: queryParams => {
console.log('Filters changed:', queryParams);
// Fetch data or update URL
},
}
);
Function | Description | Parameters | Returns |
---|---|---|---|
createFilterFactory(options?) |
Creates a collection of filter factory functions | options : Configuration with optional keyTransformer function |
Filter factory object |
useFilters(filters, options?) |
Creates a reactive filter object | filters : Map of filter configurationsoptions : Optional configuration |
Reactive filter object |
Option | Type | Description | Default |
---|---|---|---|
keyTransformer |
Function | Transforms filter keys for URL parameters | key => key |
Example:
// For Laravel Spatie Query Builder format
const f = createFilterFactory({
keyTransformer: key => `filter[${key}]`
});
Option | Type | Description | Default |
---|---|---|---|
delimiter |
String | Character used to separate multiple values | ',' |
onApply |
Function | Callback function triggered when filters are applied via get() |
|
preserveQueryOrder |
Boolean | Preserves URL query parameter order when generating query objects | true |
undefined |
Example:
const filters = useFilters(
{ /* filter definitions */ },
{
delimiter: ';',
onApply: queryParams => {
// Called when filters.get() is invoked
console.log('Applied filters:', queryParams);
// Update URL, fetch data, etc.
}
}
);
Filter | Description | Default Value | Example |
---|---|---|---|
single() |
Single value filter | null |
f.single('default') |
multiple() |
Array-based filter | [] |
f.multiple(['default']) |
range() |
Filter with from/to values | {from: null, to: null} |
f.range({from: 10, to: 50}) |
custom() |
User-defined filter behavior | Based on implementation | See Custom Filter example |
Method | Description | Parameters | Returns |
---|---|---|---|
get() |
Triggers the onApply callback | None | void |
toSearchParams() |
Converts filters to URLSearchParams | None | URLSearchParams object |
toQueryObject(transformKeys?) |
Creates object with filter key-values | transformKeys : Boolean (default: true) |
Object with filter parameters |
toOrderedQueryObject(transformKeys?) |
Creates object with filter key-values, preserving URL parameter order | transformKeys : Boolean (default: true) |
Object with ordered filter parameters |
data() |
Returns plain object of filter values | None | Filter values object |
has(filter, value) |
Checks if value exists in filter | filter : Filter keyvalue : Value to check |
Boolean |
clear(filterKey, shouldGet?) |
Resets filter(s) to default | filterKey : String/Array of keysshouldGet : Boolean to trigger onApply |
Void |
clearAll() |
Resets all filters | None | Void |
setOptions(newOptions) |
Updates filter options | newOptions : New options object |
Void |
const f = createFilterFactory();
const filters = useFilters({
search: f.single(''),
category: f.single(null),
brands: f.multiple([])
});
// Set filter values
filters.search = 'laptop';
filters.category = 'electronics';
filters.brands = ['apple', 'samsung'];
// Check if filter has value
console.log(filters.has('brands', 'apple')); // true
// Get all filter values as query parameters
const queryParams = filters.get();
import { watch } from 'vue';
import { useDebounceFn } from '@vueuse/core';
// Option 1: Simple watcher
watch(
filters,
() => filters.get(), // Trigger onApply callback
{ deep: true }
);
// Option 2: Debounced watcher
const debouncedApplyFilters = useDebounceFn(() => {
filters.get();
}, 300);
watch(filters, () => debouncedApplyFilters(), { deep: true });
// Reset a single filter
filters.clear('category');
// Reset multiple filters
filters.clear(['brands', 'price']);
// Reset all filters
filters.clearAll();
<script setup>
import { useFilters, createFilterFactory } from '@veebisepad/vue-query-filters';
const f = createFilterFactory();
const filters = useFilters({
search: f.single(''),
categories: f.multiple([]),
priceRange: f.range({ from: null, to: null })
});
function applyFilters() {
filters.get();
}
</script>
<template>
<div>
<h2>Product Filters</h2>
<!-- Single filter -->
<input v-model="filters.search" type="text" placeholder="Search products..." />
<!-- Multiple filter -->
<div>
<label><input type="checkbox" value="electronics" v-model="filters.categories" /> Electronics</label>
<label><input type="checkbox" value="clothing" v-model="filters.categories" /> Clothing</label>
</div>
<!-- Range filter -->
<div>
<input v-model.number="filters.priceRange.from" type="number" placeholder="Min" />
<input v-model.number="filters.priceRange.to" type="number" placeholder="Max" />
</div>
<button @click="applyFilters">Apply Filters</button>
<button @click="filters.clearAll()">Reset All</button>
</div>
</template>
import { createFilterFactory, useFilters } from '@veebisepad/vue-query-filters';
// 1) Create a filter factory
const factory = createFilterFactory();
// 2) Define a custom filter
const myCustomFilter = factory.custom({
defaultValue: 123,
parseQueryParam(value, delimiter) {
// Expected to parse 'enabled,value' from the incoming query string
// and return the parsed filter value
},
serializeQueryParam(currentValue, delimiter) {
// Expected to generate a string for the URL query parameter
// or return null if the filter is inactive
},
hasValue(filterValue, candidate) {
// Expected to determine if the current filter value
// 'matches' a given candidate
},
});
// 3) Implement the custom filter in useFilters
const filters = useFilters(
{
specialSetting: myCustomFilter,
},
{
onApply(queryParams) {
// Triggered when filters.get() is called
console.log('Custom filter changed:', queryParams);
},
}
);
// 4) Use the custom filter
filters.specialSetting = 234;
filters.get(); // calls onApply with updated query params
import { useFilters, createFilterFactory } from '@veebisepad/vue-query-filters';
import { router } from '@inertiajs/vue3';
import { watch } from 'vue';
// Create filter factory with key transformer for Laravel Query Builder
const f = createFilterFactory({
keyTransformer: key => `filter[${key}]`
});
const filters = useFilters(
{
name: f.single(),
category: f.multiple(),
price: f.range(),
sort: f.single('-created_at')
},
{
onApply: queryParams => {
// Navigate with Inertia preserving state
router.visit(route('products.index', queryParams), {
preserveState: true,
preserveScroll: true,
only: ['products']
});
}
}
);
// Auto-apply filters when they change
watch(filters, () => filters.get(), { deep: true });
import { useFilters, createFilterFactory } from '@veebisepad/vue-query-filters';
import type { SingleFilter, MultipleFilter, RangeFilter } from '@veebisepad/vue-query-filters';
const f = createFilterFactory();
const filters = useFilters({
category: f.single<string>('all'),
brands: f.multiple<string>([]),
price: f.range<number>({ from: null, to: null }),
});
MIT