@progress/kendo-angular-treeview
Version:
Kendo UI TreeView for Angular
165 lines (164 loc) • 7 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 { Directive, Input, Optional, Host } from '@angular/core';
import { getter } from '@progress/kendo-common';
import { anyChanged, isChanged } from '@progress/kendo-angular-common';
import { DataBoundComponent } from './data-bound-component';
import { isArrayWithAtLeastOneItem, isPresent } from './utils';
import { of } from 'rxjs';
import { HierarchyEditingService } from './drag-and-drop/editing-services/hierarchy-editing.service';
import { DragAndDropDirective } from './drag-and-drop/drag-and-drop.directive';
import { isVisible } from './default-callbacks';
import { IndexBuilderService } from './index-builder.service';
import { FilteringBase } from './filtering-base';
import * as i0 from "@angular/core";
import * as i1 from "./data-bound-component";
import * as i2 from "./drag-and-drop/drag-and-drop.directive";
const indexBuilder = new IndexBuilderService();
const mapToWrappers = (currentLevelNodes, childrenField, parent = null, parentIndex = '') => {
if (!isArrayWithAtLeastOneItem(currentLevelNodes)) {
return [];
}
return currentLevelNodes.map((node, idx) => {
const index = indexBuilder.nodeIndex(idx.toString(), parentIndex);
const wrapper = {
dataItem: node,
index,
parent,
visible: true
};
wrapper.children = mapToWrappers(getter(childrenField)(node), childrenField, wrapper, index);
return wrapper;
});
};
/**
* Represents a directive that handles hierarchical data binding and provides built-in filtering functionality for the TreeView.
*
* Use this directive to bind hierarchical data where child nodes are nested within parent nodes.
* The directive also enables the built-in filter input and automatic filter handling when used with the `filterable` property.
*
* @example
* ```html
* <kendo-treeview
* kendoTreeViewHierarchyBinding
* childrenField="items"
* textField="text"
* [nodes]="data"
* [filterable]="true">
* </kendo-treeview>
* ```
*
* @remarks
* Applied to: {@link TreeViewComponent}
*/
export class HierarchyBindingDirective extends FilteringBase {
component;
dragAndDropDirective;
/**
* Specifies the field name of the parent node that holds the child data items.
*/
set childrenField(value) {
if (!value) {
throw new Error("'childrenField' cannot be empty");
}
this._childrenField = value;
}
/**
* Specifies the field name of the parent node that holds the child data items.
*/
get childrenField() {
return this._childrenField;
}
/**
* Specifies the hierarchical data displayed by the TreeView.
*/
set nodes(nodes) {
// Needs to be a setter so that it can be accessed via `super` call (typescript v5)
this._nodes = nodes;
}
get nodes() {
return this._nodes;
}
_nodes;
/**
* @hidden
* A callback which determines whether a TreeView node should be rendered as hidden.
*/
set isVisible(fn) {
this.component.isVisible = fn;
}
/**
* @hidden
*/
loadOnDemand = true;
_childrenField;
originalData = [];
constructor(component, dragAndDropDirective) {
super(component);
this.component = component;
this.dragAndDropDirective = dragAndDropDirective;
const shouldFilter = !isPresent(this.dragAndDropDirective);
this.component.isVisible = shouldFilter ? (node) => this.visibleNodes.has(node) : isVisible;
}
ngOnInit() {
if (isPresent(this.childrenField)) {
this.component.children = item => of(getter(this.childrenField)(item));
this.component.hasChildren = item => {
const children = getter(this.childrenField)(item);
return Boolean(children && children.length);
};
this.component.editService = new HierarchyEditingService(this);
this.component.filterChange.subscribe(this.handleFilterChange.bind(this));
if (this.component.filter) {
this.handleFilterChange(this.component.filter);
}
if (!this.loadOnDemand && isPresent(this.component.preloadChildNodes)) {
this.component.preloadChildNodes();
}
}
}
ngOnChanges(changes) {
if (isChanged('childrenField', changes, false)) {
this.nodes = this.originalData;
this.updateNodes(this.originalData);
}
if (isChanged('nodes', changes, false)) {
this.updateNodes(changes['nodes'].currentValue);
}
// should react to changes.loadOnDemand as well - should preload the data or clear the already cached items
if (anyChanged(['nodes', 'loadOnDemand'], changes) && !this.loadOnDemand && isPresent(this.component.preloadChildNodes)) {
this.component.preloadChildNodes();
}
}
/**
* @hidden
*/
updateNodes(values) {
this.originalData = values || [];
this.filterData = mapToWrappers(values, this.childrenField) || [];
this.updateVisibleNodes(this.filterData);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HierarchyBindingDirective, deps: [{ token: i1.DataBoundComponent }, { token: i2.DragAndDropDirective, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: HierarchyBindingDirective, isStandalone: true, selector: "[kendoTreeViewHierarchyBinding]", inputs: { childrenField: "childrenField", nodes: "nodes", isVisible: "isVisible", loadOnDemand: "loadOnDemand" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HierarchyBindingDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoTreeViewHierarchyBinding]',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i1.DataBoundComponent }, { type: i2.DragAndDropDirective, decorators: [{
type: Optional
}, {
type: Host
}] }]; }, propDecorators: { childrenField: [{
type: Input
}], nodes: [{
type: Input
}], isVisible: [{
type: Input
}], loadOnDemand: [{
type: Input
}] } });