Client-side Filtering
The most common way to use filtering in Infinite Table is by configuring filters for columns (this works both for client-side and server-side filtering).
If the DataSource data
property is a function (and not an array or a Promise
), then the filtering will happen server-side by default.
Note
To force client-side filtering, you can explicitly set the filterMode="local"
property on the <DataSource />
component.
The possible values for this prop are:
filterMode="local"
- filtering will happen client-sidefilterMode="remote"
- filtering will happen remotely and thefilterValue
will be passed as a property to the parameter object sent to thedata
function.
Showing the Column Filters
In order to show the column filter editors in the column headers, you need to specify either the uncontrolled defaultFilterValue
property or the controlled filterValue
version.
This example shows remote data with local filtering - it sets filterMode="local"
on the <DataSource />
component.
In addition, the filterDelay
property is set to 0
for instant feedback.
import * as React from 'react'; import { DataSourceData, InfiniteTable, InfiniteTablePropColumns, DataSource, defaultFilterTypes, } from '@infinite-table/infinite-react'; type Developer = { id: number; firstName: string; lastName: string; currency: string; preferredLanguage: string; stack: string; canDesign: 'yes' | 'no'; salary: number; }; defaultFilterTypes.string.operators.push({ name: 'Not includes', label: 'Not Includes', fn: ({ currentValue, filterValue, emptyValues }) => { if ( emptyValues.includes(currentValue) || emptyValues.includes(filterValue) ) { return true; } return ( typeof currentValue === 'string' && typeof filterValue == 'string' && !currentValue.toLowerCase().includes(filterValue.toLowerCase()) ); }, }); const data: DataSourceData<Developer> = () => { return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', type: 'number', defaultWidth: 70, defaultFilterable: false, }, salary: { field: 'salary', type: 'number', }, firstName: { field: 'firstName', }, stack: { field: 'stack' }, currency: { field: 'currency' }, }; const domProps = { style: { height: '100%', }, }; export default () => { return ( <> <React.StrictMode> <DataSource<Developer> data={data} primaryKey="id" defaultFilterValue={[]} filterDelay={0} filterMode="local" > <InfiniteTable<Developer> domProps={domProps} columnDefaultWidth={150} columnMinWidth={50} columns={columns} /> </DataSource> </React.StrictMode> </>
Note
If you still want filtering to be enabled with the default functionality of using the filterValue
(or uncontrolled defaultFilterValue
), but want to hide the column filter editors, you can set the showColumnFilters
property to false
.
Using Filter Types
As already documented in the Understanding Filter Types section, you can specify the types of the filters the <DataSource />
will support, by using the filterTypes
property.
The default filter types are string
and number
- read the next section to see how you can add new operators to those filter types.
A filter type is basically a collection of operators available for a type of data. Each operator needs a name and a function that will be used to filter the data, when that operator is applied.
filterValue={[
{
field: 'firstName',
filter: {
type: 'string',
operator: 'includes',
value: 'John'
}
},
{
field: 'age',
filter: {
type: 'number',
operator: 'gt',
value: 30
}
}
]}
The above filter value specifies that there are 2 filters applied:
- the
firstName
column applies a filter that will only match rows withfirstName
containining the stringJohn
- the
age
column has an additional filter, that will only match rows withage
greater than30
If filterMode
is set to local
, then the filtering will happen client-side, using the filtering functions specified by includes
operator in the string
filter type and the gt
operator in the number
filter type.
Here's a snippet of code from the string
filter type showing the includes
operator:
operators: [
{
name: 'includes',
components: { Icon: /* a React Component */ },
label: 'Includes',
fn: ({ currentValue, filterValue }) => {
return (
typeof currentValue === 'string' &&
typeof filterValue == 'string' &&
currentValue.toLowerCase().includes(filterValue.toLowerCase())
);
},
},
//...
]
Let's now look at another example, of implementing a custom salary
filter type.
For this, we override the filterTypes
property of the <DataSource />
component:
const filterTypes = {
salary: {
defaultOperator: 'gt',
emptyValues: ['', null, undefined],
operators: [ /*...*/ ]
}
}
<DataSource<Developer>
filterTypes={filterTypes}
Note
When you specify new filterTypes
, the default filter types of string
and number
are still available - unless the new object contains those keys and overrides them explicitly.
The salary
column has a custom filter type, with the following operators: gt
, gte
, lt
and lte
.
import * as React from 'react'; import { DataSourceData, DataSource, InfiniteTable, InfiniteTablePropColumns, } from '@infinite-table/infinite-react'; type Developer = { id: number; firstName: string; lastName: string; currency: string; preferredLanguage: string; stack: string; canDesign: 'yes' | 'no'; salary: number; }; const data: DataSourceData<Developer> = () => { return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', type: 'number', defaultWidth: 100, }, salary: { defaultFilterable: true, field: 'salary', type: 'number', filterType: 'salary', }, firstName: { field: 'firstName', }, stack: { field: 'stack' }, currency: { field: 'currency', defaultFilterable: false }, }; function getIcon(icon: string) { return () => ( <div style={{ width: 20, display: 'flex', justifyContent: 'center', flexFlow: 'row', }} > {icon} </div> ); } const domProps = { style: { height: '100%', }, }; export default () => { return ( <> <React.StrictMode> <DataSource<Developer> data={data} primaryKey="id" defaultFilterValue={[]} filterDelay={0} filterMode="local" filterTypes={{ salary: { defaultOperator: 'gt', emptyValues: ['', null, undefined], operators: [ { name: 'gt', label: 'Greater Than', components: { Icon: getIcon('>'), }, fn: ({ currentValue, filterValue }) => { return currentValue > filterValue; }, }, { name: 'gte', components: { Icon: getIcon('>='), }, label: 'Greater Than or Equal', fn: ({ currentValue, filterValue }) => { return currentValue >= filterValue; }, }, { name: 'lt', components: { Icon: getIcon('<'), }, label: 'Less Than', fn: ({ currentValue, filterValue }) => { return currentValue < filterValue; }, }, { name: 'lte', components: { Icon: getIcon('<='), }, label: 'Less Than or Equal', fn: ({ currentValue, filterValue }) => { return currentValue <= filterValue; }, }, ], }, }} > <InfiniteTable<Developer> domProps={domProps} columnDefaultWidth={150} columnMinWidth={50} columns={columns} /> </DataSource> </React.StrictMode> </>
Customizing Default Filter Types
By default, the string
and number
filter types are available. You can import the default filter types like this:
import { defaultFilterTypes } from '@infinite-table/infinite-react';
If you want to make all your instances of InfiniteTable
have new operators for those filter types, you can simply mutate the exported defaultFilterTypes
object.
The string
columns have a new Not includes
operator.
import * as React from 'react'; import { DataSourceData, InfiniteTable, InfiniteTablePropColumns, DataSource, defaultFilterTypes, } from '@infinite-table/infinite-react'; type Developer = { id: number; firstName: string; lastName: string; currency: string; preferredLanguage: string; stack: string; canDesign: 'yes' | 'no'; salary: number; }; defaultFilterTypes.string.operators.push({ name: 'Not contains', label: 'Not Contains', fn: ({ currentValue, filterValue, emptyValues }) => { if ( emptyValues.includes(currentValue) || emptyValues.includes(filterValue) ) { return true; } return ( typeof currentValue === 'string' && typeof filterValue == 'string' && !currentValue.toLowerCase().includes(filterValue.toLowerCase()) ); }, }); const data: DataSourceData<Developer> = () => { return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', type: 'number', defaultWidth: 100, }, firstName: { field: 'firstName', }, stack: { field: 'stack' }, salary: { field: 'salary', type: 'number', }, currency: { field: 'currency', defaultFilterable: false }, }; const domProps = { style: { height: '100%', }, }; export default () => { return ( <> <React.StrictMode> <DataSource<Developer> data={data} primaryKey="id" defaultFilterValue={[]} filterDelay={0} filterMode="local" > <InfiniteTable<Developer> domProps={domProps} columnDefaultWidth={150} columnMinWidth={50} columns={columns} /> </DataSource> </React.StrictMode> </>
Note
When you specify new filterTypes
, the default filter types of string
and number
are still available - unless the new object contains those keys and override them explicitly.
Using a Filter Delay
In order to save some resources, filtering is batched by default. This is controlled by the filterDelay
prop, which, if not specified, defaults to 200
milliseconds. This means, any changes to the column filters, that happen inside a 200ms window (or the current value of filterDelay
), will be debounced and only the last value will be used to trigger a filter.
Note
If you want to prevent debouncing/batching filter values, you can set filterDelay
to 0
.
Note
API calls to setColumnFilter
or clearColumnFilter
are not batched.
Using a Filter Function Instead of the Column Filters
For client-side rendering, it's possible that instead of showing a column filter bar, you use a custom filterFunction
to filter the data.
In this case, the filtering will happen client-side ... of course 🤦♂️.
Loads data from remote location but will only show rows that have id > 100
.
import * as React from 'react'; import { DataSourceData, DataSource, InfiniteTable, InfiniteTablePropColumns, } from '@infinite-table/infinite-react'; type Developer = { id: number; firstName: string; lastName: string; currency: string; preferredLanguage: string; stack: string; canDesign: 'yes' | 'no'; salary: number; }; const data: DataSourceData<Developer> = () => { return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', type: 'number', defaultWidth: 100, }, salary: { defaultFilterable: true, field: 'salary', type: 'number', filterType: 'salary', }, firstName: { field: 'firstName', }, stack: { field: 'stack' }, currency: { field: 'currency', defaultFilterable: false }, }; const domProps = { style: { height: '100%', }, }; export default () => { return ( <> <React.StrictMode> <DataSource<Developer> data={data} primaryKey="id" filterFunction={({ data }) => { return data.id > 100; }} > <InfiniteTable<Developer> domProps={domProps} columnDefaultWidth={150} columnMinWidth={50} columns={columns} /> </DataSource> </React.StrictMode> </>