@progress/kendo-angular-treelist
Version:
Kendo UI TreeList for Angular - Display hierarchical data in an Angular tree grid view that supports sorting, filtering, paging, and much more.
181 lines (180 loc) • 6.34 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { Input, Directive } from '@angular/core';
import { Subscription } from 'rxjs';
import { process, aggregateBy } from '@progress/kendo-data-query';
import { isChanged } from '@progress/kendo-angular-common';
import { DataBoundTreeComponent } from './data-bound-tree-component';
import { anyChanged } from '../utils';
import { RowReorderService } from './../row-reordering/row-reorder.service';
import * as i0 from "@angular/core";
import * as i1 from "./data-bound-tree-component";
import * as i2 from "./../row-reordering/row-reorder.service";
/**
* @hidden
*/
export class BaseBindingDirective {
component;
rowReorderService;
/**
* Sets the descriptors that sort the data.
*/
set sort(value) {
this.component.sort = this.state.sort = value;
}
/**
* Sets the descriptor that filters the data.
*/
set filter(value) {
this.component.filter = this.state.filter = value;
}
/**
* Sets the descriptor that aggregates the data.
*/
set aggregate(value) {
this._aggregate = value;
}
state = {};
dataChanged;
cache = new Map();
originalData = [];
_aggregate;
subscriptions = new Subscription();
constructor(component, rowReorderService) {
this.component = component;
this.rowReorderService = rowReorderService;
this.component.fetchChildren = this.fetchChildren.bind(this);
this.component.hasChildren = this.hasChildren.bind(this);
}
/**
* @hidden
*/
ngOnInit() {
this.applyState(this.state);
this.subscriptions.add(this.component.dataStateChange
.subscribe(this.onStateChange.bind(this)));
this.component.rowReorderable && this.subscriptions.add(this.rowReorderService.rowReorder
.subscribe(this.onRowReorder.bind(this)));
}
/**
* @hidden
*/
ngOnDestroy() {
if (this.subscriptions) {
this.subscriptions.unsubscribe();
}
}
ngDoCheck() {
if (this.dataChanged) {
this.dataChanged = false;
this.rebind();
}
}
/**
* @hidden
*/
ngOnChanges(changes) {
if (anyChanged(['sort', 'filter', 'aggregate'], changes)) {
this.rebind();
}
if (isChanged('data', changes, false)) {
this.updateData(changes['data'].currentValue);
}
}
/**
* @hidden
*/
onStateChange(state) {
this.applyState(state);
this.rebind();
}
/**
* Clears the directive cache and reloads the component data.
*/
rebind() {
this.cache.clear();
this.component.data = this.fetchChildren();
}
onRowReorder(_event) { }
applyState({ sort, filter }) {
this.sort = sort;
this.filter = filter;
}
fetchChildren(item) {
const key = this.itemKey(item);
if (this.cache.has(key)) {
return this.cache.get(key);
}
const children = this.getChildren(item);
let items = this.filterItems(children);
let aggregates;
if (items.length) {
if (this.state.sort) {
items = process(items, { sort: this.state.sort }).data;
}
if (this._aggregate) {
aggregates = this.calculateAggregates(items);
}
}
const result = {
data: items,
aggregates: aggregates
};
this.cache.set(key, result);
return result;
}
hasChildren(item) {
const items = this.fetchChildren(item).data;
return items && items.length > 0;
}
filterItems(items) {
const filter = this.state.filter;
const hasFilter = filter && filter.filters && filter.filters.length > 0;
if (hasFilter) {
const filter = {
logic: 'or',
filters: [this.state.filter, {
operator: (item) => {
const children = this.fetchChildren(item);
return Boolean(children.data && children.data.length);
}
}]
};
return process(items, { filter: filter }).data;
}
return items;
}
calculateAggregates(items) {
const list = [];
const toAdd = items.slice(0);
while (toAdd.length) {
const current = toAdd.shift();
list.push(current);
if (this.hasChildren(current)) {
toAdd.push(...this.fetchChildren(current).data);
}
}
// can accumulate from children aggregates except for average
// for average we need the children count that have numeric value
// maybe move the aggregates implementation here ???
return aggregateBy(list, this._aggregate);
}
updateData(value) {
this.originalData = value || [];
this.dataChanged = true;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: BaseBindingDirective, deps: [{ token: i1.DataBoundTreeComponent }, { token: i2.RowReorderService }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: BaseBindingDirective, inputs: { sort: "sort", filter: "filter", aggregate: "aggregate" }, usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: BaseBindingDirective, decorators: [{
type: Directive,
args: [{}]
}], ctorParameters: function () { return [{ type: i1.DataBoundTreeComponent }, { type: i2.RowReorderService }]; }, propDecorators: { sort: [{
type: Input
}], filter: [{
type: Input
}], aggregate: [{
type: Input
}] } });