Reusable Custom Filter Components

The Grid provides the base for creating an advanced, custom-filter user interface that you can use across multiple Grids.

Depending on the filtering mode of the Kendo UI Grid for Angular, the approach varies.

Row Filtering

To implement a custom and reusable DropDownList filter-cell component in the Grid:

  1. Subclass the custom filter component from BaseFilterCellComponent to utilize the existing functions.

        export class DropDownListFilterComponent extends BaseFilterCellComponent {
            //...
        }
  2. Pass a FilterService through its constructor as required by the BaseFilterCellComponent abstract class. This service is responsible for the communication with the host Grid component.

        constructor(filterService: FilterService) {
            super(filterService);
        }
  3. Add the root CompositeFilterDescriptor as required by the component so you cab add or modify it with the filter descriptor that will be built.

        @Input() public filter: CompositeFilterDescriptor;
  4. Provide the appropriate textField, valueField, and data input properties to reuse the filter component on different column fields. Pass the properties to the DropDownList component.

        @Input() public data: any[];
        @Input() public textField: string;
        @Input() public valueField: string;
  5. (Optional) Construct a default item that will be used when the user wants to remove the filter.

        public get defaultItem(): any {
            return {
                [this.textField]: "Select item...",
                [this.valueField]: null
            };
        }
  6. Add the DropDownList component to the template.

        @Component({
            selector: 'my-dropdown-filter',
            template: `
                <kendo-dropdownlist
                    [data]="data"
                    (valueChange)="onChange($event)"
                    [defaultItem]="defaultItem"
                    [value]="selectedValue"
                    [valuePrimitive]="true"
                    [textField]="textField"
                    [valueField]="valueField">
                </kendo-dropdownlist>
            `
        })
        export class DropDownListFilterComponent extends BaseFilterCellComponent {
            //...
        }
  7. Hook the valueChange event handler and modify the root filter descriptor.

    The applyFilter, removeFilter, and updateFilter methods are provided by the base class. Their role is to update the root filter, remove the child filter for the field, and override the child filter.

        public onChange(value: any): void {
            this.applyFilter(
                value === null ? // if value of the default item
                    this.removeFilter(this.valueField) : // remove the filter
                    this.updateFilter({ // otherwise add/modify the filter for the field with the value
                        field: this.valueField,
                        operator: "eq",
                        value: value
                    })
            ); // and update the root filter
        }
  8. Apply the custom-filter component to the application component.

        <kendo-grid-column field="Category.CategoryName" title="Category">
            <ng-template kendoGridFilterCellTemplate let-filter>
                <my-dropdown-filter
                    [filter]="filter"
                    [data]="distinctCategories"
                    textField="CategoryName"
                    valueField="CategoryID">
                </my-dropdown-filter>
            </ng-template>
        </kendo-grid-column>

The following example demonstrates the full implementation of the approach.

Example
View Source
Edit In Stackblitz  
Change Theme:

Multi-Checkbox Menu Filtering

To create a filterable multi-checkbox menu component and use it in the FilterMenu template of the Grid:

  1. Create a custom component that will take the filter of the current column and the FilterService as inputs. The data to which the custom list will be bound can be a collection of the distinct Grid data from the respective field. The custom filter component can work with both primitive values and complex objects.

        <kendo-grid-column field="ProductName" title="Product Name">
          <ng-template kendoGridFilterMenuTemplate
                let-column="column"
                let-filter="filter"
                let-filterService="filterService"
                >
                <multicheck-filter
                  [isPrimitive]="true"
                  [field]="column.field"
                  [filterService]="filterService"
                  [currentFilter]="filter"
                  [data]="distinctPrimitive(column.field)"></multicheck-filter>
            </ng-template>
        </kendo-grid-column>
  2. Loop through the data to create a list of items with checkboxes and text. The items will correspond to the values of the respective Grid column. Attach the required handlers and bindings that will manage the selection and deselection of items.

        <ul>
            <li *ngIf="showFilter">
                <input class="k-textbox" (input)="onInput($event)" />
            </li>
            <li
                *ngFor="let item of currentData; let i = index;"
                (click)="onSelectionChange(isPrimitive ? item : item[valueField])"
                [ngClass]="{'k-state-selected': isItemSelected(item)}">
                <input
                type="checkbox"
                id="chk-{{isPrimitive ? item : item[valueField]}}"
                class="k-checkbox"
                [checked]="isItemSelected(item)" />
                <label
                class="k-multiselect-checkbox k-checkbox-label"
                for="chk-{{isPrimitive ? item : item[valueField]}}">
                    {{ isPrimitive ? item : item[textField] }}
                </label>
            </li>
        </ul>
  3. Monitor the selected items and call the filterService.filter() method accordingly.

        public onSelectionChange(item) {
            if(this.value.some(x => x === item)) {
                this.value = this.value.filter(x => x !== item);
            } else {
                this.value.push(item);
            }
    
            this.filterService.filter({
                filters: this.value.map(value => ({
                    field: this.field,
                    operator: 'eq',
                    value
                })),
                logic: 'or'
            });
        }
  4. (Optional) Add a custom filter for the list of options.

        <li *ngIf="showFilter">
            <input class="k-textbox" (input)="onInput($event)" />
        </li>
        public onInput(e) {
            this.currentData = distinct([
            ...this.currentData.filter(dataItem => this.value.some(val => val === this.valueAccessor(dataItem))),
            ...filterBy(this.data, {
                operator: 'contains',
                field: this.textField,
                value: e.target.value
            })],
            this.textField
            );
        }

The following example demonstrates the full implementation of the approach.

Example
View Source
Edit In Stackblitz  
Change Theme:

Filter Menu with Popup

By default, the filter menu closes when the user clicks outside the menu content. Such behavior is typical for components which render their popup in the body or the root componentfor example, the DatePicker.

You can override the default behavior and prevent the menu from closing by injecting the SinglePopupService into the current filter component and prevent its onClose event.

Example
View Source
Edit In Stackblitz  
Change Theme:

Filtering by Date Ranges

The Grid enables you to implement filtering which is based on a date period in the Grid.

  1. Add the DateRange component to the custom cell template of the filter component by utilizing the filter row component.

    <kendo-daterange>
        <kendo-dateinput
        kendoDateRangeStartInput
            [value]="start"
            (valueChange)="filterRange($event, end)">
        </kendo-dateinput>
        -
        <kendo-dateinput
            kendoDateRangeEndInput
            [value]="end"
            (valueChange)="filterRange(start, $event)">
        </kendo-dateinput>
    </kendo-daterange>
  2. Add a CompositeFilterDescriptor which describes the selected date range to the root filter of the Grid by using the valueChange event of the DateInput component.

    public filterRange(start: Date, end: Date): void {
        this.filter = this.removeFilter(this.field);
    
        const filters = [];
    
        if (start) {
            filters.push({
                field: this.field,
                operator: "gte",
                value: start
            });
        }
    
        if (end) {
            filters.push({
                field: this.field,
                operator: "lte",
                value: end
            });
        }
    
        const root = this.filter || {
            logic: "and",
            filters: []
        };
    
        if (filters.length) {
            root.filters.push(...filters);
        }
    
        this.filterService.filter(root);
    }

The following example demonstrates the full implementation of the approach.

Example
View Source
Edit In Stackblitz  
Change Theme: