New to Kendo UI for Angular? Start a free 30-day trial

Reusable Custom Filter Components

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

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

Row Filtering

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

  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 TreeList 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: unknown[];
        @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.field) : // remove the filter
                    this.updateFilter({ // otherwise add/modify the filter for the field with the value
                        field: this.field,
                        operator: "eq",
                        value: value
                    })
            ); // and update the root filter
        }
  8. Apply the custom-filter component to the application component.

        <kendo-treelist-column field="title" title="Title" [width]="180">
            <ng-template kendoTreeListFilterCellTemplate let-filter>
                <my-dropdown-filter
                    [filter]="filter"
                    [data]="titles"
                    field="title"
                    textField="text"
                    valueField="value">
                </my-dropdown-filter>
            </ng-template>
        </kendo-treelist-column>

The following example demonstrates the full implementation of the approach.

Example
View Source
Change Theme:

Multi-Checkbox Menu Filtering

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

  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 TreeList data from the respective field. The custom filter component can work with both primitive values and complex objects.

        <kendo-treelist-column  field="name" title="Name">
          <ng-template kendoTreeListFilterMenuTemplate
                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-treelist-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 TreeList column. Attach the required handlers and bindings that will manage the selection and deselection of items.

        <ul>
            <li *ngIf="showFilter">
                <input kendoTextBox (input)="onInput($event)" />
            </li>
            <li
                *ngFor="let item of currentData; let i = index;"
                (click)="onSelectionChange(isPrimitive ? item : item[valueField])"
                [ngClass]="{'k-selected': isItemSelected(item)}">
                <input
                type="checkbox"
                id="chk-{{isPrimitive ? item : item[valueField]}}"
                kendoCheckBox
                [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 kendoTextBox (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
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 component—for 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
Change Theme:

Filtering by Date Ranges

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

  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 TreeList 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
Change Theme: