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:
-
Subclass the custom filter component from
BaseFilterCellComponent
to utilize the existing functions.export class DropDownListFilterComponent extends BaseFilterCellComponent { //... }
-
Pass a
FilterService
through its constructor as required by theBaseFilterCellComponent
abstract class. This service is responsible for the communication with the host TreeList component.constructor(filterService: FilterService) { super(filterService); }
-
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;
-
Provide the appropriate
textField
,valueField
, anddata
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;
-
(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 }; }
-
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 { //... }
-
Hook the
valueChange
event handler and modify the root filter descriptor.The
applyFilter
,removeFilter
, andupdateFilter
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 }
-
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.
Multi-Checkbox Menu Filtering
To create a filterable multi-checkbox menu component and use it in the FilterMenu
template of the TreeList:
-
Create a custom component that will take the
filter
of the current column and theFilterService
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>
-
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>
-
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' }); }
-
(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.
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.
Filtering by Date Ranges
The TreeList enables you to implement filtering which is based on a date period in the TreeList.
-
Add the
DateRange
component to the custom cell template of thefilter
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>
-
Add a
CompositeFilterDescriptor
which describes the selected date range to the root filter of the TreeList by using thevalueChange
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.