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: 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.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.

import { Component } from '@angular/core';
import { Employee, employees } from './employees';
import { FilterService } from '@progress/kendo-angular-treelist';

const distinctTitles = data => data
    .map(item => ({
        value: item.title,
        text: item.title
    }))
    .filter((item, idx, arr) => arr.findIndex(i => i.value === item.value) === idx);

@Component({
    selector: 'my-app',
    template: `
        <kendo-treelist [kendoTreeListFlatBinding]="data" idField="id" parentIdField="managerId"
                kendoTreeListExpandable
                [height]="410"
                [filterable]="true"
            >
            <kendo-treelist-column [expandable]="true" field="name" title="Name" [width]="250">
            </kendo-treelist-column>
            <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>
            <kendo-treelist-column field="phone" title="Phone" [width]="180">
            </kendo-treelist-column>
        </kendo-treelist>
    `
})
export class AppComponent {
    public data: Employee[] = employees;
    public titles = distinctTitles(employees);
}

import { Component, Input } from '@angular/core';
import { CompositeFilterDescriptor } from '@progress/kendo-data-query';
import { FilterService, BaseFilterCellComponent } from '@progress/kendo-angular-treelist';

@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 {

    public get selectedValue(): any {
        const filter = this.filterByField(this.field);
        return filter ? filter.value : null;
    }

    @Input() public filter: CompositeFilterDescriptor;
    @Input() public data: any[];
    @Input() public textField: string;
    @Input() public valueField: string;
    @Input() public field: string;

    public get defaultItem(): any {
        return {
            [this.textField]: 'Select item...',
            [this.valueField]: null
        };
    }

    constructor(filterService: FilterService) {
        super(filterService);
    }

    public onChange(value: any): void {
        this.applyFilter(
            value === null ? // value of the default item
                this.removeFilter(this.field) : // remove the filter
                this.updateFilter({ // add a filter for the field with the value
                    field: this.field,
                    operator: 'eq',
                    value: value
                })
        ); // update the root filter
    }
}
import { NgModule, enableProdMode } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { TreeListModule } from '@progress/kendo-angular-treelist';
import { DropDownListModule } from '@progress/kendo-angular-dropdowns';

import { AppComponent } from './app.component';
import { DropDownListFilterComponent } from './dropdownlist-filter.component';

// enableProdMode();

@NgModule({
  bootstrap: [
    AppComponent
  ],
  declarations: [
    AppComponent,
    DropDownListFilterComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    TreeListModule,
    DropDownListModule
  ]
})
export class AppModule { }
import { AppModule } from './app.module';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);


export interface Employee {
    id: number;
    managerId?: number;
    name: string;
    title: string;
    phone: string;
    hireDate?: Date;
}

export const employees: Employee[] = [
    {
        id: 1,
        name: 'Daryl Sweeney',
        title: 'Chief Executive Officer',
        phone: '(555) 924-9726',
        managerId: null,
        hireDate: new Date('2019-01-15')
    },
    {
        id: 2,
        name: 'Guy Wooten',
        title: 'Chief Technical Officer',
        phone: '(438) 738-4935',
        managerId: 1,
        hireDate: new Date('2019-02-19')
    },
    {
        id: 32,
        name: 'Buffy Weber',
        title: 'VP, Engineering',
        phone: '(699) 838-6121',
        managerId: 2,
        hireDate: new Date('2019-04-13')
    },
    {
        id: 11,
        name: 'Hyacinth Hood',
        title: 'Team Lead',
        phone: '(889) 345-2438',
        managerId: 32,
        hireDate: new Date('2018-01-17')
    },
    {
        id: 60,
        name: 'Akeem Carr',
        title: 'Junior Software Developer',
        phone: '(738) 136-2814',
        managerId: 11,
        hireDate: new Date('2018-01-18')
    },
    {
        id: 78,
        name: 'Rinah Simon',
        title: 'Software Developer',
        phone: '(285) 912-5271',
        managerId: 11,
        hireDate: new Date('2018-03-17')
    },
    {
        id: 42,
        name: 'Gage Daniels',
        title: 'Software Architect',
        phone: '(107) 290-6260',
        managerId: 32,
        hireDate: new Date('2019-03-14')
    },
    {
        id: 43,
        name: 'Constance Vazquez',
        title: 'Director, Engineering',
        phone: '(800) 301-1978',
        managerId: 32,
        hireDate: new Date('2018-03-18')
    },
    {
        id: 46,
        name: 'Darrel Solis',
        title: 'Team Lead',
        phone: '(327) 977-0216',
        managerId: 43,
        hireDate: new Date('2019-04-15')
    },
    {
        id: 47,
        name: 'Brian Yang',
        title: 'Senior Software Developer',
        phone: '(565) 146-5435',
        managerId: 46,
        hireDate: new Date('2019-02-21')
    },
    {
        id: 50,
        name: 'Lillian Bradshaw',
        title: 'Software Developer',
        phone: '(323) 509-3479',
        managerId: 46,
        hireDate: new Date('2019-05-23')
    },
    {
        id: 51,
        name: 'Christian Palmer',
        title: 'Technical Lead',
        phone: '(490) 421-8718',
        managerId: 46,
        hireDate: new Date('2019-04-16')
    },
    {
        id: 55,
        name: 'Summer Mosley',
        title: 'QA Engineer',
        phone: '(784) 962-2301',
        managerId: 46,
        hireDate: new Date('2019-09-21')
    },
    {
        id: 56,
        name: 'Barry Ayers',
        title: 'Software Developer',
        phone: '(452) 373-9227',
        managerId: 46,
        hireDate: new Date('2018-04-16')
    },
    {
        id: 59,
        name: 'Keiko Espinoza',
        title: 'Junior QA Engineer',
        phone: '(226) 600-5305',
        managerId: 46,
        hireDate: new Date('2018-01-22')
    },
    {
        id: 61,
        name: 'Candace Pickett',
        title: 'Support Officer',
        phone: '(120) 117-7475',
        managerId: 46,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 63,
        name: 'Mia Caldwell',
        title: 'Team Lead',
        phone: '(848) 636-6470',
        managerId: 43,
        hireDate: new Date('2018-07-17')
    },
    {
        id: 65,
        name: 'Thomas Terry',
        title: 'Senior Enterprise Support Officer',
        phone: '(764) 831-4248',
        managerId: 63,
        hireDate: new Date('2018-07-14')
    },
    {
        id: 67,
        name: 'Ruth Downs',
        title: 'Senior Software Developer',
        phone: '(138) 991-1440',
        managerId: 63,
        hireDate: new Date('2018-08-14')
    },
    {
        id: 70,
        name: 'Yasir Wilder',
        title: 'Senior QA Engineer',
        phone: '(759) 701-8665',
        managerId: 63,
        hireDate: new Date('2019-08-17')
    },
    {
        id: 71,
        name: 'Flavia Short',
        title: 'Support Officer',
        phone: '(370) 133-9238',
        managerId: 63,
        hireDate: new Date('2018-06-15')
    },
    {
        id: 74,
        name: 'Aaron Roach',
        title: 'Junior Software Developer',
        phone: '(958) 717-9230',
        managerId: 63,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 75,
        name: 'Eric Russell',
        title: 'Software Developer',
        phone: '(516) 575-8505',
        managerId: 63,
        hireDate: new Date('2019-09-13')
    },
    {
        id: 76,
        name: 'Cheyenne Olson',
        title: 'Software Developer',
        phone: '(241) 645-0257',
        managerId: 63,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 77,
        name: 'Shaine Avila',
        title: 'UI Designer',
        phone: '(844) 435-1360',
        managerId: 63,
        hireDate: new Date('2018-01-22')
    },
    {
        id: 81,
        name: 'Chantale Long',
        title: 'Senior QA Engineer',
        phone: '(252) 419-6891',
        managerId: 63,
        hireDate: new Date('2018-09-14')
    },
    {
        id: 83,
        name: 'Dane Cruz',
        title: 'Junior Software Developer',
        phone: '(946) 701-6165',
        managerId: 63,
        hireDate: new Date('2019-03-15')
    },
    {
        id: 84,
        name: 'Regan Patterson',
        title: 'Technical Writer',
        phone: '(265) 946-1765',
        managerId: 63,
        hireDate: new Date('2019-05-17')
    },
    {
        id: 85,
        name: 'Drew Mckay',
        title: 'Senior Software Developer',
        phone: '(327) 293-0162',
        managerId: 63,
        hireDate: new Date('2019-05-21')
    },
    {
        id: 88,
        name: 'Bevis Miller',
        title: 'Senior Software Developer',
        phone: '(525) 557-0169',
        managerId: 63,
        hireDate: new Date('2018-08-15')
    },
    {
        id: 89,
        name: 'Bruce Mccarty',
        title: 'Support Officer',
        phone: '(936) 777-8730',
        managerId: 63,
        hireDate: new Date('2019-10-21')
    },
    {
        id: 90,
        name: 'Ocean Blair',
        title: 'Team Lead',
        phone: '(343) 586-6614',
        managerId: 43,
        hireDate: new Date('2018-06-20')
    },
    {
        id: 91,
        name: 'Guinevere Osborn',
        title: 'Software Developer',
        phone: '(424) 741-0006',
        managerId: 90,
        hireDate: new Date('2019-06-17')
    },
    {
        id: 92,
        name: 'Olga Strong',
        title: 'Graphic Designer',
        phone: '(949) 417-1168',
        managerId: 90,
        hireDate: new Date('2018-06-15')
    },
    {
        id: 93,
        name: 'Robert Orr',
        title: 'Support Officer',
        phone: '(977) 341-3721',
        managerId: 90,
        hireDate: new Date('2018-06-22')
    },
    {
        id: 95,
        name: 'Odette Sears',
        title: 'Senior Software Developer',
        phone: '(264) 818-6576',
        managerId: 90,
        hireDate: new Date('2019-05-20')
    },
    {
        id: 45,
        name: 'Zelda Medina',
        title: 'QA Architect',
        phone: '(563) 359-6023',
        managerId: 32,
        hireDate: new Date('2018-08-16')
    },
    {
        id: 3,
        name: 'Priscilla Frank',
        title: 'Chief Product Officer',
        phone: '(217) 280-5300',
        managerId: 1,
        hireDate: new Date('2019-04-22')
    },
    {
        id: 4,
        name: 'Ursula Holmes',
        title: 'EVP, Product Strategy',
        phone: '(370) 983-8796',
        managerId: 3,
        hireDate: new Date('2018-01-15')
    },
    {
        id: 24,
        name: 'Melvin Carrillo',
        title: 'Director, Developer Relations',
        phone: '(344) 496-9555',
        managerId: 3,
        hireDate: new Date('2018-01-17')
    },
    {
        id: 29,
        name: 'Martha Chavez',
        title: 'Developer Advocate',
        phone: '(140) 772-7509',
        managerId: 24,
        hireDate: new Date('2018-05-14')
    },
    {
        id: 30,
        name: 'Oren Fox',
        title: 'Developer Advocate',
        phone: '(714) 284-2408',
        managerId: 24,
        hireDate: new Date('2018-07-19')
    },
    {
        id: 41,
        name: 'Amos Barr',
        title: 'Developer Advocate',
        phone: '(996) 587-8405',
        managerId: 24,
        hireDate: new Date('2019-01-16')
    }
  ];

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 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.

import { Component } from '@angular/core';
import { distinct } from '@progress/kendo-data-query';
import { Employee, employees } from './employees';

@Component({
    selector: 'my-app',
    template: `
        <kendo-treelist [kendoTreeListFlatBinding]="data" idField="id" parentIdField="managerId"
                kendoTreeListExpandable
                [height]="410"
                filterable="menu"
            >
            <kendo-treelist-column [expandable]="true" field="name" title="Name" [width]="250">
              <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>

            <kendo-treelist-column field="title" title="Title" [width]="180">
              <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>

            <kendo-treelist-column field="phone" title="Phone" [width]="180">
              <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>
        </kendo-treelist>
    `
})
export class AppComponent {
    public data: Employee[] = employees;

    public distinctPrimitive(fieldName: string): any {
      return distinct(employees, fieldName).map(item => item[fieldName]);
    }
}


import { Component, Input, Output, EventEmitter, AfterViewInit } from '@angular/core';
import { CompositeFilterDescriptor, distinct, filterBy, FilterDescriptor } from '@progress/kendo-data-query';
import { FilterService } from '@progress/kendo-angular-treelist';

@Component({
  selector: 'multicheck-filter',
  template: `
    <ul>
      <li *ngIf="showFilter">
        <input class="k-textbox" (input)="onInput($event)" />
      </li>
      <li #itemElement
        *ngFor="let item of currentData; let i = index;"
        (click)="onSelectionChange(valueAccessor(item), itemElement)"
        [ngClass]="{'k-state-selected': isItemSelected(item)}">
        <input
          type="checkbox"
          id="chk-{{valueAccessor(item)}}"
          (focus)="onFocus(itemElement)"
          class="k-checkbox"
          [checked]="isItemSelected(item)" />
        <label
          class="k-multiselect-checkbox k-checkbox-label"
          for="chk-{{valueAccessor(item)}}">
            {{ textAccessor(item) }}
        </label>
      </li>
    </ul>
  `,
  styles: [`
    ul {
      list-style-type: none;
      height: 200px;
      overflow-y: scroll;
      padding-left: 0;
      padding-right: 12px;
    }

    ul>li {
      padding: 8px 12px;
      border: 1px solid rgba(0,0,0,.08);
      border-bottom: none;
    }

    ul>li:last-of-type {
      border-bottom: 1px solid rgba(0,0,0,.08);
    }

    .k-multiselect-checkbox {
      pointer-events: none;
    }
  `]
})
export class MultiCheckFilterComponent implements AfterViewInit {
  @Input() public isPrimitive: boolean;
  @Input() public currentFilter: CompositeFilterDescriptor;
  @Input() public data;
  @Input() public textField;
  @Input() public valueField;
  @Input() public filterService: FilterService;
  @Input() public field: string;
  @Output() public valueChange = new EventEmitter<number[]>();

  public currentData: any;
  public showFilter = true;
  private value: any[] = [];

  protected textAccessor = (dataItem: any) => this.isPrimitive ? dataItem : dataItem[this.textField];
  protected valueAccessor = (dataItem: any) => this.isPrimitive ? dataItem : dataItem[this.valueField];

  public ngAfterViewInit() {
    this.currentData = this.data;
    this.value = this.currentFilter.filters.map((f: FilterDescriptor) => f.value);

    this.showFilter = typeof this.textAccessor(this.currentData[0]) === 'string';
  }

  public isItemSelected(item) {
    return this.value.some(x => x === this.valueAccessor(item));
  }

  public onSelectionChange(item, li) {
    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'
    });

    this.onFocus(li);
  }

  public onInput(e: any) {
    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
      );
  }

  public onFocus(li: any): void {
    const ul = li.parentNode;
    const below = ul.scrollTop + ul.offsetHeight < li.offsetTop + li.offsetHeight;
    const above = li.offsetTop < ul.scrollTop;

    // Scroll to focused checkbox
    if (below || above) {
      ul.scrollTop = li.offsetTop;
    }
  }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { TreeListModule } from '@progress/kendo-angular-treelist';
import { AppComponent } from './app.component';
import { MultiCheckFilterComponent } from './multicheck-filter.component';

@NgModule({
  bootstrap: [
    AppComponent
  ],
  declarations: [
    AppComponent, MultiCheckFilterComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    TreeListModule
  ]
})
export class AppModule { }
import { AppModule } from './app.module';
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

enableProdMode();

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

export interface Employee {
    id: number;
    managerId?: number;
    name: string;
    title: string;
    phone: string;
    hireDate?: Date;
}

export const employees: Employee[] = [
    {
        id: 1,
        name: 'Daryl Sweeney',
        title: 'Chief Executive Officer',
        phone: '(555) 924-9726',
        managerId: null,
        hireDate: new Date('2019-01-15')
    },
    {
        id: 2,
        name: 'Guy Wooten',
        title: 'Chief Technical Officer',
        phone: '(438) 738-4935',
        managerId: 1,
        hireDate: new Date('2019-02-19')
    },
    {
        id: 32,
        name: 'Buffy Weber',
        title: 'VP, Engineering',
        phone: '(699) 838-6121',
        managerId: 2,
        hireDate: new Date('2019-04-13')
    },
    {
        id: 11,
        name: 'Hyacinth Hood',
        title: 'Team Lead',
        phone: '(889) 345-2438',
        managerId: 32,
        hireDate: new Date('2018-01-17')
    },
    {
        id: 60,
        name: 'Akeem Carr',
        title: 'Junior Software Developer',
        phone: '(738) 136-2814',
        managerId: 11,
        hireDate: new Date('2018-01-18')
    },
    {
        id: 78,
        name: 'Rinah Simon',
        title: 'Software Developer',
        phone: '(285) 912-5271',
        managerId: 11,
        hireDate: new Date('2018-03-17')
    },
    {
        id: 42,
        name: 'Gage Daniels',
        title: 'Software Architect',
        phone: '(107) 290-6260',
        managerId: 32,
        hireDate: new Date('2019-03-14')
    },
    {
        id: 43,
        name: 'Constance Vazquez',
        title: 'Director, Engineering',
        phone: '(800) 301-1978',
        managerId: 32,
        hireDate: new Date('2018-03-18')
    },
    {
        id: 46,
        name: 'Darrel Solis',
        title: 'Team Lead',
        phone: '(327) 977-0216',
        managerId: 43,
        hireDate: new Date('2019-04-15')
    },
    {
        id: 47,
        name: 'Brian Yang',
        title: 'Senior Software Developer',
        phone: '(565) 146-5435',
        managerId: 46,
        hireDate: new Date('2019-02-21')
    },
    {
        id: 50,
        name: 'Lillian Bradshaw',
        title: 'Software Developer',
        phone: '(323) 509-3479',
        managerId: 46,
        hireDate: new Date('2019-05-23')
    },
    {
        id: 51,
        name: 'Christian Palmer',
        title: 'Technical Lead',
        phone: '(490) 421-8718',
        managerId: 46,
        hireDate: new Date('2019-04-16')
    },
    {
        id: 55,
        name: 'Summer Mosley',
        title: 'QA Engineer',
        phone: '(784) 962-2301',
        managerId: 46,
        hireDate: new Date('2019-09-21')
    },
    {
        id: 56,
        name: 'Barry Ayers',
        title: 'Software Developer',
        phone: '(452) 373-9227',
        managerId: 46,
        hireDate: new Date('2018-04-16')
    },
    {
        id: 59,
        name: 'Keiko Espinoza',
        title: 'Junior QA Engineer',
        phone: '(226) 600-5305',
        managerId: 46,
        hireDate: new Date('2018-01-22')
    },
    {
        id: 61,
        name: 'Candace Pickett',
        title: 'Support Officer',
        phone: '(120) 117-7475',
        managerId: 46,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 63,
        name: 'Mia Caldwell',
        title: 'Team Lead',
        phone: '(848) 636-6470',
        managerId: 43,
        hireDate: new Date('2018-07-17')
    },
    {
        id: 65,
        name: 'Thomas Terry',
        title: 'Senior Enterprise Support Officer',
        phone: '(764) 831-4248',
        managerId: 63,
        hireDate: new Date('2018-07-14')
    },
    {
        id: 67,
        name: 'Ruth Downs',
        title: 'Senior Software Developer',
        phone: '(138) 991-1440',
        managerId: 63,
        hireDate: new Date('2018-08-14')
    },
    {
        id: 70,
        name: 'Yasir Wilder',
        title: 'Senior QA Engineer',
        phone: '(759) 701-8665',
        managerId: 63,
        hireDate: new Date('2019-08-17')
    },
    {
        id: 71,
        name: 'Flavia Short',
        title: 'Support Officer',
        phone: '(370) 133-9238',
        managerId: 63,
        hireDate: new Date('2018-06-15')
    },
    {
        id: 74,
        name: 'Aaron Roach',
        title: 'Junior Software Developer',
        phone: '(958) 717-9230',
        managerId: 63,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 75,
        name: 'Eric Russell',
        title: 'Software Developer',
        phone: '(516) 575-8505',
        managerId: 63,
        hireDate: new Date('2019-09-13')
    },
    {
        id: 76,
        name: 'Cheyenne Olson',
        title: 'Software Developer',
        phone: '(241) 645-0257',
        managerId: 63,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 77,
        name: 'Shaine Avila',
        title: 'UI Designer',
        phone: '(844) 435-1360',
        managerId: 63,
        hireDate: new Date('2018-01-22')
    },
    {
        id: 81,
        name: 'Chantale Long',
        title: 'Senior QA Engineer',
        phone: '(252) 419-6891',
        managerId: 63,
        hireDate: new Date('2018-09-14')
    },
    {
        id: 83,
        name: 'Dane Cruz',
        title: 'Junior Software Developer',
        phone: '(946) 701-6165',
        managerId: 63,
        hireDate: new Date('2019-03-15')
    },
    {
        id: 84,
        name: 'Regan Patterson',
        title: 'Technical Writer',
        phone: '(265) 946-1765',
        managerId: 63,
        hireDate: new Date('2019-05-17')
    },
    {
        id: 85,
        name: 'Drew Mckay',
        title: 'Senior Software Developer',
        phone: '(327) 293-0162',
        managerId: 63,
        hireDate: new Date('2019-05-21')
    },
    {
        id: 88,
        name: 'Bevis Miller',
        title: 'Senior Software Developer',
        phone: '(525) 557-0169',
        managerId: 63,
        hireDate: new Date('2018-08-15')
    },
    {
        id: 89,
        name: 'Bruce Mccarty',
        title: 'Support Officer',
        phone: '(936) 777-8730',
        managerId: 63,
        hireDate: new Date('2019-10-21')
    },
    {
        id: 90,
        name: 'Ocean Blair',
        title: 'Team Lead',
        phone: '(343) 586-6614',
        managerId: 43,
        hireDate: new Date('2018-06-20')
    },
    {
        id: 91,
        name: 'Guinevere Osborn',
        title: 'Software Developer',
        phone: '(424) 741-0006',
        managerId: 90,
        hireDate: new Date('2019-06-17')
    },
    {
        id: 92,
        name: 'Olga Strong',
        title: 'Graphic Designer',
        phone: '(949) 417-1168',
        managerId: 90,
        hireDate: new Date('2018-06-15')
    },
    {
        id: 93,
        name: 'Robert Orr',
        title: 'Support Officer',
        phone: '(977) 341-3721',
        managerId: 90,
        hireDate: new Date('2018-06-22')
    },
    {
        id: 95,
        name: 'Odette Sears',
        title: 'Senior Software Developer',
        phone: '(264) 818-6576',
        managerId: 90,
        hireDate: new Date('2019-05-20')
    },
    {
        id: 45,
        name: 'Zelda Medina',
        title: 'QA Architect',
        phone: '(563) 359-6023',
        managerId: 32,
        hireDate: new Date('2018-08-16')
    },
    {
        id: 3,
        name: 'Priscilla Frank',
        title: 'Chief Product Officer',
        phone: '(217) 280-5300',
        managerId: 1,
        hireDate: new Date('2019-04-22')
    },
    {
        id: 4,
        name: 'Ursula Holmes',
        title: 'EVP, Product Strategy',
        phone: '(370) 983-8796',
        managerId: 3,
        hireDate: new Date('2018-01-15')
    },
    {
        id: 24,
        name: 'Melvin Carrillo',
        title: 'Director, Developer Relations',
        phone: '(344) 496-9555',
        managerId: 3,
        hireDate: new Date('2018-01-17')
    },
    {
        id: 29,
        name: 'Martha Chavez',
        title: 'Developer Advocate',
        phone: '(140) 772-7509',
        managerId: 24,
        hireDate: new Date('2018-05-14')
    },
    {
        id: 30,
        name: 'Oren Fox',
        title: 'Developer Advocate',
        phone: '(714) 284-2408',
        managerId: 24,
        hireDate: new Date('2018-07-19')
    },
    {
        id: 41,
        name: 'Amos Barr',
        title: 'Developer Advocate',
        phone: '(996) 587-8405',
        managerId: 24,
        hireDate: new Date('2019-01-16')
    }
  ];

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.

import { Component } from '@angular/core';
import { Employee, employees } from './employees';

@Component({
    selector: 'my-app',
    template: `
        <kendo-treelist [kendoTreeListFlatBinding]="data" idField="id" parentIdField="managerId"
                kendoTreeListExpandable
                [height]="410"
                filterable="menu"
            >
            <kendo-treelist-column [expandable]="true" field="name" title="Name" [width]="250">
            </kendo-treelist-column>
            <kendo-treelist-column field="title" title="Title" [width]="180">
            </kendo-treelist-column>
            <kendo-treelist-column field="hireDate" title="Hire Date" [width]="180" format="d">
                <ng-template kendoTreeListFilterMenuTemplate let-filter let-column="column" let-filterService="filterService">
                    <date-range-filter
                        [field]="column.field"
                        [filter]="filter"
                        [filterService]="filterService">
                    </date-range-filter>
                </ng-template>
            </kendo-treelist-column>
        </kendo-treelist>
    `
})
export class AppComponent {
    public data: Employee[] = employees;
}
import { Component, Input, OnInit, OnDestroy, ElementRef } from '@angular/core';
import { CompositeFilterDescriptor } from '@progress/kendo-data-query';
import { FilterService, SinglePopupService, PopupCloseEvent } from '@progress/kendo-angular-treelist';
import { addDays } from '@progress/kendo-date-math';

const closest = (node: any, predicate: any): any => {
    while (node && !predicate(node)) {
        node = node.parentNode;
    }

    return node;
};

@Component({
    selector: 'date-range-filter',
    template: `
        <div class="k-form">
            <label class="k-form-field">
                <span>After</span>
                <kendo-datepicker (valueChange)="onStartChange($event)"
                    [(ngModel)]="start" [max]="max" [popupSettings]="popupSettings">
                </kendo-datepicker>
            </label>
            <label class="k-form-field">
                <span>Before</span>
                <kendo-datepicker (valueChange)="onEndChange($event)"
                    [(ngModel)]="end" [min]="min" [popupSettings]="popupSettings">
                </kendo-datepicker>
            </label>
        </div>
   `,
   styles: [`
        .k-form {
            padding: 5px;
        }
   `]
})
export class DateRangeFilterComponent implements OnInit, OnDestroy {

    @Input() public filter: CompositeFilterDescriptor;
    @Input() public filterService: FilterService;
    @Input() public field: string;

    public start: any;
    public end: any;

    public get min(): any {
        return this.start ? addDays(this.start, 1) : null;
    }

    public get max(): any {
        return this.end ? addDays(this.end, -1) : null;
    }

    public popupSettings: any = {
        popupClass: 'date-range-filter'
    };

    private popupSubscription: any;

    constructor(private element: ElementRef,
                private popupService: SinglePopupService) {

        // Handle the service onClose event and prevent the menu from closing when the datepickers are still active.
        this.popupSubscription = popupService.onClose.subscribe((e: PopupCloseEvent) => {
            if (document.activeElement && closest(document.activeElement,
                node => node === this.element.nativeElement || (String(node.className).indexOf('date-range-filter') >= 0))) {
                e.preventDefault();
            }
        });
    }

    public ngOnInit(): void {
        this.start = this.findValue('gte');
        this.end = this.findValue('lte');
    }

    public ngOnDestroy(): void {
        this.popupSubscription.unsubscribe();
    }

    public onStartChange(value: any): void {
        this.filterRange(value, this.end);
    }

    public onEndChange(value: any): void {
      this.filterRange(this.start, value);
    }

    private findValue(operator) {
      const filter = this.filter.filters.filter(x => x.field === this.field && x.operator === operator)[0];
      return filter ? filter.value : null;
    }

    private filterRange(start, end) {
        const filters = [];

        if (start && (!end || start < end)) {
            filters.push({
              field: this.field,
              operator: "gte",
              value: start
            });
            this.start = start;
        }

        if (end && (!start || start < end)) {
            filters.push({
              field: this.field,
              operator: "lte",
              value: end
            });
            this.end = end;
        }

        this.filterService.filter({
            logic: "and",
            filters: filters
        });
    }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule } from '@angular/forms';

import { TreeListModule } from '@progress/kendo-angular-treelist';
import { DatePickerModule } from '@progress/kendo-angular-dateinputs';
import { AppComponent } from './app.component';
import { DateRangeFilterComponent } from './date-range-filter.component';

@NgModule({
  bootstrap: [
    AppComponent
  ],
  declarations: [
    AppComponent, DateRangeFilterComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    TreeListModule,
    DatePickerModule
  ]
})
export class AppModule { }
import { AppModule } from './app.module';
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

enableProdMode();

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

export interface Employee {
    id: number;
    managerId?: number;
    name: string;
    title: string;
    phone: string;
    hireDate?: Date;
}

export const employees: Employee[] = [
    {
        id: 1,
        name: 'Daryl Sweeney',
        title: 'Chief Executive Officer',
        phone: '(555) 924-9726',
        managerId: null,
        hireDate: new Date('2019-01-15')
    },
    {
        id: 2,
        name: 'Guy Wooten',
        title: 'Chief Technical Officer',
        phone: '(438) 738-4935',
        managerId: 1,
        hireDate: new Date('2019-02-19')
    },
    {
        id: 32,
        name: 'Buffy Weber',
        title: 'VP, Engineering',
        phone: '(699) 838-6121',
        managerId: 2,
        hireDate: new Date('2019-04-13')
    },
    {
        id: 11,
        name: 'Hyacinth Hood',
        title: 'Team Lead',
        phone: '(889) 345-2438',
        managerId: 32,
        hireDate: new Date('2018-01-17')
    },
    {
        id: 60,
        name: 'Akeem Carr',
        title: 'Junior Software Developer',
        phone: '(738) 136-2814',
        managerId: 11,
        hireDate: new Date('2018-01-18')
    },
    {
        id: 78,
        name: 'Rinah Simon',
        title: 'Software Developer',
        phone: '(285) 912-5271',
        managerId: 11,
        hireDate: new Date('2018-03-17')
    },
    {
        id: 42,
        name: 'Gage Daniels',
        title: 'Software Architect',
        phone: '(107) 290-6260',
        managerId: 32,
        hireDate: new Date('2019-03-14')
    },
    {
        id: 43,
        name: 'Constance Vazquez',
        title: 'Director, Engineering',
        phone: '(800) 301-1978',
        managerId: 32,
        hireDate: new Date('2018-03-18')
    },
    {
        id: 46,
        name: 'Darrel Solis',
        title: 'Team Lead',
        phone: '(327) 977-0216',
        managerId: 43,
        hireDate: new Date('2019-04-15')
    },
    {
        id: 47,
        name: 'Brian Yang',
        title: 'Senior Software Developer',
        phone: '(565) 146-5435',
        managerId: 46,
        hireDate: new Date('2019-02-21')
    },
    {
        id: 50,
        name: 'Lillian Bradshaw',
        title: 'Software Developer',
        phone: '(323) 509-3479',
        managerId: 46,
        hireDate: new Date('2019-05-23')
    },
    {
        id: 51,
        name: 'Christian Palmer',
        title: 'Technical Lead',
        phone: '(490) 421-8718',
        managerId: 46,
        hireDate: new Date('2019-04-16')
    },
    {
        id: 55,
        name: 'Summer Mosley',
        title: 'QA Engineer',
        phone: '(784) 962-2301',
        managerId: 46,
        hireDate: new Date('2019-09-21')
    },
    {
        id: 56,
        name: 'Barry Ayers',
        title: 'Software Developer',
        phone: '(452) 373-9227',
        managerId: 46,
        hireDate: new Date('2018-04-16')
    },
    {
        id: 59,
        name: 'Keiko Espinoza',
        title: 'Junior QA Engineer',
        phone: '(226) 600-5305',
        managerId: 46,
        hireDate: new Date('2018-01-22')
    },
    {
        id: 61,
        name: 'Candace Pickett',
        title: 'Support Officer',
        phone: '(120) 117-7475',
        managerId: 46,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 63,
        name: 'Mia Caldwell',
        title: 'Team Lead',
        phone: '(848) 636-6470',
        managerId: 43,
        hireDate: new Date('2018-07-17')
    },
    {
        id: 65,
        name: 'Thomas Terry',
        title: 'Senior Enterprise Support Officer',
        phone: '(764) 831-4248',
        managerId: 63,
        hireDate: new Date('2018-07-14')
    },
    {
        id: 67,
        name: 'Ruth Downs',
        title: 'Senior Software Developer',
        phone: '(138) 991-1440',
        managerId: 63,
        hireDate: new Date('2018-08-14')
    },
    {
        id: 70,
        name: 'Yasir Wilder',
        title: 'Senior QA Engineer',
        phone: '(759) 701-8665',
        managerId: 63,
        hireDate: new Date('2019-08-17')
    },
    {
        id: 71,
        name: 'Flavia Short',
        title: 'Support Officer',
        phone: '(370) 133-9238',
        managerId: 63,
        hireDate: new Date('2018-06-15')
    },
    {
        id: 74,
        name: 'Aaron Roach',
        title: 'Junior Software Developer',
        phone: '(958) 717-9230',
        managerId: 63,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 75,
        name: 'Eric Russell',
        title: 'Software Developer',
        phone: '(516) 575-8505',
        managerId: 63,
        hireDate: new Date('2019-09-13')
    },
    {
        id: 76,
        name: 'Cheyenne Olson',
        title: 'Software Developer',
        phone: '(241) 645-0257',
        managerId: 63,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 77,
        name: 'Shaine Avila',
        title: 'UI Designer',
        phone: '(844) 435-1360',
        managerId: 63,
        hireDate: new Date('2018-01-22')
    },
    {
        id: 81,
        name: 'Chantale Long',
        title: 'Senior QA Engineer',
        phone: '(252) 419-6891',
        managerId: 63,
        hireDate: new Date('2018-09-14')
    },
    {
        id: 83,
        name: 'Dane Cruz',
        title: 'Junior Software Developer',
        phone: '(946) 701-6165',
        managerId: 63,
        hireDate: new Date('2019-03-15')
    },
    {
        id: 84,
        name: 'Regan Patterson',
        title: 'Technical Writer',
        phone: '(265) 946-1765',
        managerId: 63,
        hireDate: new Date('2019-05-17')
    },
    {
        id: 85,
        name: 'Drew Mckay',
        title: 'Senior Software Developer',
        phone: '(327) 293-0162',
        managerId: 63,
        hireDate: new Date('2019-05-21')
    },
    {
        id: 88,
        name: 'Bevis Miller',
        title: 'Senior Software Developer',
        phone: '(525) 557-0169',
        managerId: 63,
        hireDate: new Date('2018-08-15')
    },
    {
        id: 89,
        name: 'Bruce Mccarty',
        title: 'Support Officer',
        phone: '(936) 777-8730',
        managerId: 63,
        hireDate: new Date('2019-10-21')
    },
    {
        id: 90,
        name: 'Ocean Blair',
        title: 'Team Lead',
        phone: '(343) 586-6614',
        managerId: 43,
        hireDate: new Date('2018-06-20')
    },
    {
        id: 91,
        name: 'Guinevere Osborn',
        title: 'Software Developer',
        phone: '(424) 741-0006',
        managerId: 90,
        hireDate: new Date('2019-06-17')
    },
    {
        id: 92,
        name: 'Olga Strong',
        title: 'Graphic Designer',
        phone: '(949) 417-1168',
        managerId: 90,
        hireDate: new Date('2018-06-15')
    },
    {
        id: 93,
        name: 'Robert Orr',
        title: 'Support Officer',
        phone: '(977) 341-3721',
        managerId: 90,
        hireDate: new Date('2018-06-22')
    },
    {
        id: 95,
        name: 'Odette Sears',
        title: 'Senior Software Developer',
        phone: '(264) 818-6576',
        managerId: 90,
        hireDate: new Date('2019-05-20')
    },
    {
        id: 45,
        name: 'Zelda Medina',
        title: 'QA Architect',
        phone: '(563) 359-6023',
        managerId: 32,
        hireDate: new Date('2018-08-16')
    },
    {
        id: 3,
        name: 'Priscilla Frank',
        title: 'Chief Product Officer',
        phone: '(217) 280-5300',
        managerId: 1,
        hireDate: new Date('2019-04-22')
    },
    {
        id: 4,
        name: 'Ursula Holmes',
        title: 'EVP, Product Strategy',
        phone: '(370) 983-8796',
        managerId: 3,
        hireDate: new Date('2018-01-15')
    },
    {
        id: 24,
        name: 'Melvin Carrillo',
        title: 'Director, Developer Relations',
        phone: '(344) 496-9555',
        managerId: 3,
        hireDate: new Date('2018-01-17')
    },
    {
        id: 29,
        name: 'Martha Chavez',
        title: 'Developer Advocate',
        phone: '(140) 772-7509',
        managerId: 24,
        hireDate: new Date('2018-05-14')
    },
    {
        id: 30,
        name: 'Oren Fox',
        title: 'Developer Advocate',
        phone: '(714) 284-2408',
        managerId: 24,
        hireDate: new Date('2018-07-19')
    },
    {
        id: 41,
        name: 'Amos Barr',
        title: 'Developer Advocate',
        phone: '(996) 587-8405',
        managerId: 24,
        hireDate: new Date('2019-01-16')
    }
  ];

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.

import { Component } from '@angular/core';
import { Employee, employees } from './employees';

@Component({
    selector: 'my-app',
    template: `
        <kendo-treelist [kendoTreeListFlatBinding]="data" idField="id" parentIdField="managerId"
                kendoTreeListExpandable
                [height]="410"
                [filterable]="true"
            >
            <kendo-treelist-column [expandable]="true" field="name" title="Name" [width]="250">
            </kendo-treelist-column>

            <kendo-treelist-column field="hireDate" title="Hire Date" format="d">
                <ng-template kendoTreeListFilterCellTemplate let-filter>
                    <date-range-filter-cell
                        class="date-range-filter"
                        [filter]="filter"
                        field="hireDate">
                    </date-range-filter-cell>
                </ng-template>
            </kendo-treelist-column>
        </kendo-treelist>
    `
})
export class AppComponent {
    public data: Employee[] = employees;
}

import { Component, Input } from "@angular/core";
import { BaseFilterCellComponent, FilterService } from "@progress/kendo-angular-treelist";
import { FilterDescriptor, CompositeFilterDescriptor } from "@progress/kendo-data-query/dist/es/main";

@Component({
    selector: 'date-range-filter-cell',
    styles: [`
        kendo-daterange > kendo-dateinput.range-filter {
            display: inline-block;
        }
        .k-button {
            margin-left: 5px;
        }
    ` ],
    template: `
        <kendo-daterange>
           <kendo-dateinput
                class="range-filter"
                kendoDateRangeStartInput
                [value]="start"
                (valueChange)="filterRange($event, end)"
                style="width:120px">
            </kendo-dateinput>
            -
            <kendo-dateinput
                class="range-filter"
                kendoDateRangeEndInput
                [value]="end"
                (valueChange)="filterRange(start, $event)"
                style="width:120px">
            </kendo-dateinput>
        </kendo-daterange>
        <button
            *ngIf="hasFilter"
            class="k-button k-button-icon"
            title="Clear"
            (click)="clearFilter()">
            <span class="k-icon k-i-filter-clear"></span>
        </button>
    `
})
export class DateRangeFilterCellComponent extends BaseFilterCellComponent {
    @Input()
    public filter: CompositeFilterDescriptor;

    @Input()
    public field: string;

    constructor(filterService: FilterService) {
        super(filterService);
    }

    public get start(): Date {
        const first = this.findByOperator("gte");

        return (first || <FilterDescriptor>{}).value;
    }

    public get end(): Date {
        const end = this.findByOperator("lte");
        return (end || <FilterDescriptor>{}).value;
    }

    public get hasFilter(): boolean {
        return this.filtersByField(this.field).length > 0;
    }

    public clearFilter(): void {
        this.filterService.filter(
            this.removeFilter(this.field)
        );
    }

    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);
    }

    private findByOperator(op: string): FilterDescriptor {
        return this.filtersByField(this.field)
            .filter(({ operator }) => operator === op)[0];
    }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule } from '@angular/forms';

import { TreeListModule } from '@progress/kendo-angular-treelist';
import { DateRangeModule, DateInputModule } from '@progress/kendo-angular-dateinputs';
import { AppComponent } from './app.component';
import { DateRangeFilterCellComponent } from './date-range-cell-filter.component';


@NgModule({
  bootstrap: [
    AppComponent
  ],
  declarations: [
    AppComponent, DateRangeFilterCellComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    TreeListModule,
    DateRangeModule,
    DateInputModule
  ]
})
export class AppModule { }
import { AppModule } from './app.module';
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

enableProdMode();

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

export interface Employee {
    id: number;
    managerId?: number;
    name: string;
    title: string;
    phone: string;
    hireDate?: Date;
}

export const employees: Employee[] = [
    {
        id: 1,
        name: 'Daryl Sweeney',
        title: 'Chief Executive Officer',
        phone: '(555) 924-9726',
        managerId: null,
        hireDate: new Date('2019-01-15')
    },
    {
        id: 2,
        name: 'Guy Wooten',
        title: 'Chief Technical Officer',
        phone: '(438) 738-4935',
        managerId: 1,
        hireDate: new Date('2019-02-19')
    },
    {
        id: 32,
        name: 'Buffy Weber',
        title: 'VP, Engineering',
        phone: '(699) 838-6121',
        managerId: 2,
        hireDate: new Date('2019-04-13')
    },
    {
        id: 11,
        name: 'Hyacinth Hood',
        title: 'Team Lead',
        phone: '(889) 345-2438',
        managerId: 32,
        hireDate: new Date('2018-01-17')
    },
    {
        id: 60,
        name: 'Akeem Carr',
        title: 'Junior Software Developer',
        phone: '(738) 136-2814',
        managerId: 11,
        hireDate: new Date('2018-01-18')
    },
    {
        id: 78,
        name: 'Rinah Simon',
        title: 'Software Developer',
        phone: '(285) 912-5271',
        managerId: 11,
        hireDate: new Date('2018-03-17')
    },
    {
        id: 42,
        name: 'Gage Daniels',
        title: 'Software Architect',
        phone: '(107) 290-6260',
        managerId: 32,
        hireDate: new Date('2019-03-14')
    },
    {
        id: 43,
        name: 'Constance Vazquez',
        title: 'Director, Engineering',
        phone: '(800) 301-1978',
        managerId: 32,
        hireDate: new Date('2018-03-18')
    },
    {
        id: 46,
        name: 'Darrel Solis',
        title: 'Team Lead',
        phone: '(327) 977-0216',
        managerId: 43,
        hireDate: new Date('2019-04-15')
    },
    {
        id: 47,
        name: 'Brian Yang',
        title: 'Senior Software Developer',
        phone: '(565) 146-5435',
        managerId: 46,
        hireDate: new Date('2019-02-21')
    },
    {
        id: 50,
        name: 'Lillian Bradshaw',
        title: 'Software Developer',
        phone: '(323) 509-3479',
        managerId: 46,
        hireDate: new Date('2019-05-23')
    },
    {
        id: 51,
        name: 'Christian Palmer',
        title: 'Technical Lead',
        phone: '(490) 421-8718',
        managerId: 46,
        hireDate: new Date('2019-04-16')
    },
    {
        id: 55,
        name: 'Summer Mosley',
        title: 'QA Engineer',
        phone: '(784) 962-2301',
        managerId: 46,
        hireDate: new Date('2019-09-21')
    },
    {
        id: 56,
        name: 'Barry Ayers',
        title: 'Software Developer',
        phone: '(452) 373-9227',
        managerId: 46,
        hireDate: new Date('2018-04-16')
    },
    {
        id: 59,
        name: 'Keiko Espinoza',
        title: 'Junior QA Engineer',
        phone: '(226) 600-5305',
        managerId: 46,
        hireDate: new Date('2018-01-22')
    },
    {
        id: 61,
        name: 'Candace Pickett',
        title: 'Support Officer',
        phone: '(120) 117-7475',
        managerId: 46,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 63,
        name: 'Mia Caldwell',
        title: 'Team Lead',
        phone: '(848) 636-6470',
        managerId: 43,
        hireDate: new Date('2018-07-17')
    },
    {
        id: 65,
        name: 'Thomas Terry',
        title: 'Senior Enterprise Support Officer',
        phone: '(764) 831-4248',
        managerId: 63,
        hireDate: new Date('2018-07-14')
    },
    {
        id: 67,
        name: 'Ruth Downs',
        title: 'Senior Software Developer',
        phone: '(138) 991-1440',
        managerId: 63,
        hireDate: new Date('2018-08-14')
    },
    {
        id: 70,
        name: 'Yasir Wilder',
        title: 'Senior QA Engineer',
        phone: '(759) 701-8665',
        managerId: 63,
        hireDate: new Date('2019-08-17')
    },
    {
        id: 71,
        name: 'Flavia Short',
        title: 'Support Officer',
        phone: '(370) 133-9238',
        managerId: 63,
        hireDate: new Date('2018-06-15')
    },
    {
        id: 74,
        name: 'Aaron Roach',
        title: 'Junior Software Developer',
        phone: '(958) 717-9230',
        managerId: 63,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 75,
        name: 'Eric Russell',
        title: 'Software Developer',
        phone: '(516) 575-8505',
        managerId: 63,
        hireDate: new Date('2019-09-13')
    },
    {
        id: 76,
        name: 'Cheyenne Olson',
        title: 'Software Developer',
        phone: '(241) 645-0257',
        managerId: 63,
        hireDate: new Date('2018-09-18')
    },
    {
        id: 77,
        name: 'Shaine Avila',
        title: 'UI Designer',
        phone: '(844) 435-1360',
        managerId: 63,
        hireDate: new Date('2018-01-22')
    },
    {
        id: 81,
        name: 'Chantale Long',
        title: 'Senior QA Engineer',
        phone: '(252) 419-6891',
        managerId: 63,
        hireDate: new Date('2018-09-14')
    },
    {
        id: 83,
        name: 'Dane Cruz',
        title: 'Junior Software Developer',
        phone: '(946) 701-6165',
        managerId: 63,
        hireDate: new Date('2019-03-15')
    },
    {
        id: 84,
        name: 'Regan Patterson',
        title: 'Technical Writer',
        phone: '(265) 946-1765',
        managerId: 63,
        hireDate: new Date('2019-05-17')
    },
    {
        id: 85,
        name: 'Drew Mckay',
        title: 'Senior Software Developer',
        phone: '(327) 293-0162',
        managerId: 63,
        hireDate: new Date('2019-05-21')
    },
    {
        id: 88,
        name: 'Bevis Miller',
        title: 'Senior Software Developer',
        phone: '(525) 557-0169',
        managerId: 63,
        hireDate: new Date('2018-08-15')
    },
    {
        id: 89,
        name: 'Bruce Mccarty',
        title: 'Support Officer',
        phone: '(936) 777-8730',
        managerId: 63,
        hireDate: new Date('2019-10-21')
    },
    {
        id: 90,
        name: 'Ocean Blair',
        title: 'Team Lead',
        phone: '(343) 586-6614',
        managerId: 43,
        hireDate: new Date('2018-06-20')
    },
    {
        id: 91,
        name: 'Guinevere Osborn',
        title: 'Software Developer',
        phone: '(424) 741-0006',
        managerId: 90,
        hireDate: new Date('2019-06-17')
    },
    {
        id: 92,
        name: 'Olga Strong',
        title: 'Graphic Designer',
        phone: '(949) 417-1168',
        managerId: 90,
        hireDate: new Date('2018-06-15')
    },
    {
        id: 93,
        name: 'Robert Orr',
        title: 'Support Officer',
        phone: '(977) 341-3721',
        managerId: 90,
        hireDate: new Date('2018-06-22')
    },
    {
        id: 95,
        name: 'Odette Sears',
        title: 'Senior Software Developer',
        phone: '(264) 818-6576',
        managerId: 90,
        hireDate: new Date('2019-05-20')
    },
    {
        id: 45,
        name: 'Zelda Medina',
        title: 'QA Architect',
        phone: '(563) 359-6023',
        managerId: 32,
        hireDate: new Date('2018-08-16')
    },
    {
        id: 3,
        name: 'Priscilla Frank',
        title: 'Chief Product Officer',
        phone: '(217) 280-5300',
        managerId: 1,
        hireDate: new Date('2019-04-22')
    },
    {
        id: 4,
        name: 'Ursula Holmes',
        title: 'EVP, Product Strategy',
        phone: '(370) 983-8796',
        managerId: 3,
        hireDate: new Date('2018-01-15')
    },
    {
        id: 24,
        name: 'Melvin Carrillo',
        title: 'Director, Developer Relations',
        phone: '(344) 496-9555',
        managerId: 3,
        hireDate: new Date('2018-01-17')
    },
    {
        id: 29,
        name: 'Martha Chavez',
        title: 'Developer Advocate',
        phone: '(140) 772-7509',
        managerId: 24,
        hireDate: new Date('2018-05-14')
    },
    {
        id: 30,
        name: 'Oren Fox',
        title: 'Developer Advocate',
        phone: '(714) 284-2408',
        managerId: 24,
        hireDate: new Date('2018-07-19')
    },
    {
        id: 41,
        name: 'Amos Barr',
        title: 'Developer Advocate',
        phone: '(996) 587-8405',
        managerId: 24,
        hireDate: new Date('2019-01-16')
    }
  ];

In this article