@progress/kendo-angular-treeview
Version:
Kendo UI TreeView for Angular
148 lines (147 loc) • 6.31 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 { anyChanged, isChanged } from '@progress/kendo-angular-common';
import { Directive, Input } from '@angular/core';
import { getter } from '@progress/kendo-common';
import { of } from 'rxjs';
import { compose } from './funcs';
import { DataBoundComponent } from './data-bound-component';
import { isBlank, isNullOrEmptyString, isPresent, isArrayWithAtLeastOneItem } from './utils';
import { FlatEditingService } from "./drag-and-drop/editing-services/flat-editing.service";
import { IndexBuilderService } from './index-builder.service';
import { FilteringBase } from './filtering-base';
import * as i0 from "@angular/core";
import * as i1 from "./data-bound-component";
const findChildren = (prop, nodes, value) => nodes.filter((x) => prop(x) === value);
const indexBuilder = new IndexBuilderService();
const mapToTree = (currentLevelNodes, allNodes, parentIdField, idField, 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 = mapToTree(findChildren(getter(parentIdField), allNodes || [], getter(idField)(node)), allNodes, parentIdField, idField, wrapper, index);
return wrapper;
});
};
/**
* A directive which encapsulates the retrieval of the child nodes.
*/
export class FlatDataBindingDirective extends FilteringBase {
component;
/**
* The nodes which will be 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;
/**
* Represents the parent field whose value will be matched with the parent node.
*/
parentIdField;
/**
* Represents the unique field which identifies a node.
*/
idField;
/**
* @hidden
*/
loadOnDemand = true;
/**
* @hidden
* A callback which determines whether a TreeView node should be rendered as hidden.
*/
set isVisible(fn) {
this.component.isVisible = fn;
}
/**
* @hidden
*/
originalData = [];
constructor(component) {
super(component);
this.component = component;
this.component.isVisible = (node) => this.visibleNodes.has(node);
}
/**
* @hidden
*/
ngOnInit() {
if (isPresent(this.parentIdField) && isPresent(this.idField)) {
const fetchChildren = (node) => findChildren(getter(this.parentIdField), this.originalData || [], getter(this.idField)(node));
this.component.hasChildren = (node) => fetchChildren(node).length > 0;
this.component.children = (node) => of(fetchChildren(node));
this.component.editService = new FlatEditingService(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();
}
}
}
/**
* @hidden
*/
ngOnChanges(changes) {
if (isChanged('parentIdField', 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 || [];
if (!isNullOrEmptyString(this.parentIdField)) {
const prop = getter(this.parentIdField);
this.component.nodes = this.originalData.filter(compose(isBlank, prop));
this.filterData = mapToTree(this.component.nodes, this.originalData, this.parentIdField, this.idField);
this.updateVisibleNodes(this.filterData);
}
else {
this.component.nodes = this.originalData.slice(0);
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlatDataBindingDirective, deps: [{ token: i1.DataBoundComponent }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: FlatDataBindingDirective, isStandalone: true, selector: "[kendoTreeViewFlatDataBinding]", inputs: { nodes: "nodes", parentIdField: "parentIdField", idField: "idField", loadOnDemand: "loadOnDemand", isVisible: "isVisible" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlatDataBindingDirective, decorators: [{
type: Directive,
args: [{
selector: "[kendoTreeViewFlatDataBinding]",
standalone: true
}]
}], ctorParameters: function () { return [{ type: i1.DataBoundComponent }]; }, propDecorators: { nodes: [{
type: Input
}], parentIdField: [{
type: Input
}], idField: [{
type: Input
}], loadOnDemand: [{
type: Input
}], isVisible: [{
type: Input
}] } });