UNPKG

@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
/**----------------------------------------------------------------------------------------- * 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 }] }] } });