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:
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 Grid 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: any[]; @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.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 }
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.
Multi-Checkbox Menu Filtering
To create a filterable multi-checkbox menu component and use it in the FilterMenu
template of the Grid:
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 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>
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>
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 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.
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 Grid enables you to implement filtering which is based on a date period in the Grid.
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 Grid 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.