Filtering

To enable the filtering functionality of the MultiSelect, set the filterable property to true.

Basic Configuration

On every user modification of the input value, the MultiSelect triggers an onFilterChange event. The event argument contains the typed string value that you can use to filter the source.

import React from 'react';
import ReactDOM from 'react-dom';
import { MultiSelect } from '@progress/kendo-react-dropdowns';
import countries from './countries';
import { filterBy } from '@progress/kendo-data-query';

class AppComponent extends React.Component {
    state = {
        data: countries.slice()
    };

    filterChange = (event) => {
        this.setState({
            data: filterBy(countries.slice(), event.filter)
        });
    }

    render() {
        return (
            <MultiSelect
                data={this.state.data}
                filterable={true}
                onFilterChange={this.filterChange}
            />
        );
    }
}

ReactDOM.render(
    <AppComponent />,
    document.querySelector('my-app')
);

To filter the data after a delay, use a similar implementation. You can toggle the loading property and provide the user with a visual indication of the filtering process.

import React from 'react';
import ReactDOM from 'react-dom';
import { MultiSelect } from '@progress/kendo-react-dropdowns';
import countries from './countries';
import { filterBy } from '@progress/kendo-data-query';

const delay = 500;

class AppComponent extends React.Component {
    state = {
        data: countries.slice(),
        loading: false
    };

    filterChange = (event) => {
        clearTimeout(this.timeout);
        this.timeout = setTimeout(() => {
            this.setState({
                data: filterBy(countries.slice(), event.filter),
                loading: false
            });
        }, delay);

        this.setState({
            loading: true
        });
    }

    render() {
        return (
            <MultiSelect
                data={this.state.data}
                filterable={true}
                onFilterChange={this.filterChange}
                loading={this.state.loading}
            />
        );
    }
}

ReactDOM.render(
    <AppComponent />,
    document.querySelector('my-app')
);

Minimum Filter Length

The following example demonstrates how to update the data and open the drop-down list of the MultiSelect only after typing a minimum number of characters.

import React from 'react';
import ReactDOM from 'react-dom';
import { MultiSelect } from '@progress/kendo-react-dropdowns';
import countries from './countries';
import { filterBy } from '@progress/kendo-data-query';

class AppComponent extends React.Component {
    state = {
        data: countries.slice()
    };

    filterChange = (event) => {
        const filter = event.filter;
        const allData = countries.slice();
        const newData = filter.value.length > 3 ?
            filterBy(allData, filter) : allData;

        this.setState({
            data: newData
        });
    }

    render() {
        return (
            <MultiSelect
                data={this.state.data}
                filterable={true}
                onFilterChange={this.filterChange}
            />
        );
    }
}

ReactDOM.render(
    <AppComponent />,
    document.querySelector('my-app')
);

Filtering with Remote Data and Virtualization

The following example demonstrates how to configure the MultiSelect to use remote data along with data caching, virtual scrolling, and filtering.

import React from 'react';
import ReactDOM from 'react-dom';
import { MultiSelect } from '@progress/kendo-react-dropdowns';

const textField = 'ContactName';
const keyField = 'CustomerID';
const placeholder = 'Select customer';
const pageSize = 10;
const emptyItem = { [textField]: 'loading ...' };

const loadingData = [];
while (loadingData.length < pageSize) {
    loadingData.push({ ...emptyItem });
}

class AppComponent extends React.Component {
    baseUrl = `https://odatasampleservices.azurewebsites.net/V4/Northwind/Northwind.svc/`;
    init = { method: 'GET', accept: 'application/json', headers: [] };
    dataCaching = [];
    pendingRequest;
    requestStarted = false;
    state = {
        data: [],
        skip: 0,
        total: 0,
        filter: ''
    };

    componentDidMount() {
        this.requestData(0, this.state.filter);
    }

    requestData(skip, filter) {
        if (this.requestStarted) {
            clearTimeout(this.pendingRequest);
            this.pendingRequest = setTimeout(() => { this.requestData(skip, filter); }, 50);
            return;
        }

        const url = this.baseUrl +
            `Customers?$filter=contains(ContactName,'${filter}')&$skip=${skip}&$top=${pageSize}&$count=true`;

        this.requestStarted = true;
        fetch(url, this.init)
            .then(response => response.json())
            .then(json => {
                const total = json['@odata.count'];
                const items = [];
                json.value.forEach((element, index) => {
                    const { CustomerID, ContactName } = element;
                    const item = { [keyField]: CustomerID, [textField]: ContactName };
                    items.push(item);
                    this.dataCaching[index + skip] = item;
                });

                if (skip === this.state.skip) {
                    this.setState({
                        data: items,
                        total: total
                    });
                }
                this.requestStarted = false;
            });
    }

    onFilterChange = (event) => {
        const filter = event.filter.value;

        this.resetCach();
        this.requestData(0, filter);

        this.setState({
            data: loadingData,
            skip: 0,
            filter: filter
        });
    }

    pageChange = (event) => {
        const skip = event.page.skip;
        const filter = this.state.filter;

        if (this.shouldRequestData(skip)) {
            this.requestData(skip, filter || '');
        }

        const data = this.getCachedData(skip);

        this.setState({
            data: data,
            skip: skip
        });
    }

    render() {
        return (
            <MultiSelect
                data={this.state.data}
                textField={textField}
                dataItemKey={keyField}
                placeholder={placeholder}
                filterable={true}
                onFilterChange={this.onFilterChange}
                virtual={{
                    pageSize: pageSize,
                    skip: this.state.skip,
                    total: this.state.total
                }}
                onPageChange={this.pageChange}
                style={{ width: '600px' }}
            />
        );
    }

    componentWillUnmount() {
        this.resetCach();
    }

    shouldRequestData(skip) {
        for (let i = 0; i < pageSize; i++) {
            if (!this.dataCaching[skip + i]) {
                return true;
            }
        }
        return false;
    }

    getCachedData(skip) {
        const data = [];
        for (let i = 0; i < pageSize; i++) {
            data.push(this.dataCaching[i + skip] || { ...emptyItem });
        }
        return data;
    }

    resetCach() {
        this.dataCaching.length = 0;
    }
}

ReactDOM.render(
    <AppComponent />,
    document.querySelector('my-app')
);
 /