@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.
278 lines (277 loc) • 11 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 { Component, Input, ContentChildren, QueryList, NgZone } from '@angular/core';
import { isObservable } from 'rxjs';
import { take } from 'rxjs/operators';
import { saveAs } from '@progress/kendo-file-saver';
import { toDataURL, workbookOptions, ColumnBase } from '@progress/kendo-angular-excel-export';
import { ExcelService } from './excel.service';
import { ExcelExportEvent } from './excel-export-event';
import { LocalizationService } from '@progress/kendo-angular-l10n';
import { orderBy } from '@progress/kendo-data-query';
import { sortColumns } from '../columns/column-common';
import { ViewCollection, EXPANDED_STATE } from '../data/data.collection';
import { hasObservers } from '@progress/kendo-angular-common';
import * as i0 from "@angular/core";
import * as i1 from "./excel.service";
import * as i2 from "@progress/kendo-angular-l10n";
const hierarchyData = (view) => {
const data = view.data;
const levels = {};
const aggregates = {};
const items = [];
let depth = 0;
for (let idx = 0, dataIndex = 0; idx < data.length; idx++) {
const item = data[idx];
if (item.type === 'data') {
items.push(item.data);
levels[dataIndex] = item.level;
depth = Math.max(depth, item.level);
dataIndex++;
}
else {
aggregates[item.parentIndex] = item.aggregates;
}
}
return {
itemId: (_item, idx) => idx,
itemLevel: (_item, idx) => levels[idx],
depth: depth + 1,
aggregates,
data: items
};
};
const toExcelColumn = (column) => {
return {
title: column.title,
field: column.field,
locked: Boolean(column.locked),
width: column.width,
level: column.level,
hidden: !column.isVisible,
footerTemplate: column.footerTemplate
};
};
const toExcelColumns = (columns) => {
const result = [];
sortColumns(columns)
.forEach((column) => {
if (column.isSpanColumn) {
result.push(...toExcelColumns(column.childrenArray));
}
else {
const excelColumn = toExcelColumn(column);
if (column.isColumnGroup) {
excelColumn.children = [excelColumn].concat(toExcelColumns(column.childrenArray));
}
result.push(excelColumn);
}
});
return result;
};
const componentColumns = (component) => {
const columns = toExcelColumns(component.columns.toArray());
return orderBy(columns, [{ field: 'locked', dir: 'desc' }]);
};
/**
* Configures the settings for exporting the TreeList to Excel. [See example]({% slug excelexport_treelist %}).
*
* @example
* ```html
* <kendo-treelist [data]="data" [height]="500">
* <kendo-treelist-excel fileName="Employees.xlsx"></kendo-treelist-excel>
* </kendo-treelist>
* ```
*/
export class ExcelComponent {
excelService;
localization;
zone;
/**
* Specifies the file name of the exported Excel file.
* @default "Export.xlsx"
*/
fileName = 'Export.xlsx';
/**
* Enables or disables column filtering in the Excel file. This is different from the TreeList filtering feature.
*/
filterable;
/**
* Sets the author of the workbook.
*/
creator;
/**
* Sets the date the workbook was created. Defaults to `new Date()`.
*/
date;
/**
* If set to `true`, forwards the content to `proxyURL` even if the browser supports saving files locally.
*/
forceProxy;
/**
* The URL of the server-side proxy that streams the file to the user.
* Use a proxy if the browser cannot save files locally.
*
* Optionally, set up a proxy to reduce memory usage. This avoids copying the file contents into memory,
* but transmits it over the network instead. For this use case, set [forceProxy](#toc-forceproxy) to `true`
* to skip client-side saving even in browsers that support it.
*
* The proxy receives a POST request with specific parameters in the request body. [See example](slug:server_proxy#toc-implementations).
* The proxy returns the decoded file with the `"Content-Disposition"` header set to `attachment; filename="<fileName.xslx>"`.
*
* For details on server-side proxy usage and implementation, see the [File Saver]({% slug overview_filesaver %}) documentation.
*/
proxyURL;
/**
* The function used to get the exported data options. By default, uses the current TreeList data.
* To export data different from the current TreeList data, provide a custom function.
*/
fetchData;
/**
* Specifies if item levels in the Excel file are collapsible. Applies only if the TreeList has footers.
*/
collapsible;
/**
* Specifies if export should include all pages.
* @default true
*/
allPages = true;
/**
* Specifies if the export should expand all items or use the current TreeList state.
* @default true
*/
expandAll = true;
/**
* The options for the cells inserted before the data to indicate the hierarchy.
*/
paddingCellOptions;
/**
* @hidden
*/
columns = new QueryList();
saveSubscription;
dataSubscription;
constructor(excelService, localization, zone) {
this.excelService = excelService;
this.localization = localization;
this.zone = zone;
this.saveSubscription = excelService.saveToExcel.subscribe(this.save.bind(this));
}
ngOnDestroy() {
this.saveSubscription.unsubscribe();
if (this.dataSubscription) {
this.dataSubscription.unsubscribe();
}
}
save(component) {
const result = this.fetchData ? this.fetchData(component) : null;
this.excelService.toggleLoading(true);
this.zone.runOutsideAngular(() => {
if (result && isObservable(result.data)) {
this.dataSubscription = result.data.pipe(take(1)).subscribe((data) => {
this.dataSubscription = null;
this.exportData(component, {
data: data,
fetchChildren: result.fetchChildren,
hasChildren: result.hasChildren
});
});
}
else {
// allow the loading to be shown
setTimeout(() => {
this.exportData(component, result);
});
}
});
}
exportData(component, result) {
const viewOptions = result ? {
fields: result,
expandState: this.expandAll ? EXPANDED_STATE : component.expandStateService
} : this.componentViewOptions(component);
ViewCollection.loadView(viewOptions).subscribe((view) => {
if (!view) {
return;
}
const hierarchy = hierarchyData(view);
const options = workbookOptions({
columns: this.columns.length ? this.columns : componentColumns(component),
data: hierarchy.data,
aggregates: hierarchy.aggregates,
filterable: this.filterable,
creator: this.creator,
date: this.date,
rtl: this.localization.rtl,
collapsible: this.collapsible,
hierarchy: hierarchy,
paddingCellOptions: this.paddingCellOptions
});
const args = new ExcelExportEvent(options);
if (hasObservers(component.excelExport)) {
this.zone.run(() => {
component.excelExport.emit(args);
});
}
this.excelService.toggleLoading(false);
if (!args.isDefaultPrevented()) {
this.saveFile(options);
}
});
}
saveFile(options) {
toDataURL(options).then((dataURL) => {
saveAs(dataURL, this.fileName, {
forceProxy: this.forceProxy,
proxyURL: this.proxyURL
});
});
}
componentViewOptions(component) {
return {
fields: Object.assign(component.viewFieldAccessor(), {
pageable: !this.allPages,
skip: this.allPages ? 0 : component.skip
}),
expandState: !this.expandAll || (!this.allPages && component.pageable) ? component.expandStateService : EXPANDED_STATE,
loaded: new Map(component.view.loaded)
};
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExcelComponent, deps: [{ token: i1.ExcelService }, { token: i2.LocalizationService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ExcelComponent, isStandalone: true, selector: "kendo-treelist-excel", inputs: { fileName: "fileName", filterable: "filterable", creator: "creator", date: "date", forceProxy: "forceProxy", proxyURL: "proxyURL", fetchData: "fetchData", collapsible: "collapsible", allPages: "allPages", expandAll: "expandAll", paddingCellOptions: "paddingCellOptions" }, queries: [{ propertyName: "columns", predicate: ColumnBase, descendants: true }], ngImport: i0, template: ``, isInline: true });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExcelComponent, decorators: [{
type: Component,
args: [{
selector: 'kendo-treelist-excel',
template: ``,
standalone: true
}]
}], ctorParameters: () => [{ type: i1.ExcelService }, { type: i2.LocalizationService }, { type: i0.NgZone }], propDecorators: { fileName: [{
type: Input
}], filterable: [{
type: Input
}], creator: [{
type: Input
}], date: [{
type: Input
}], forceProxy: [{
type: Input
}], proxyURL: [{
type: Input
}], fetchData: [{
type: Input
}], collapsible: [{
type: Input
}], allPages: [{
type: Input
}], expandAll: [{
type: Input
}], paddingCellOptions: [{
type: Input
}], columns: [{
type: ContentChildren,
args: [ColumnBase, { descendants: true }]
}] } });