From: Bill Erickson Date: Sun, 13 May 2018 22:03:19 +0000 (-0400) Subject: LP#1775466 More grid / csv X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=b55326c8e98b6e80b8de88b82dfec5dabb30815e;p=working%2FEvergreen.git LP#1775466 More grid / csv Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html index 60499a9a5b..2bd0e3f1c3 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html +++ b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html @@ -80,7 +80,9 @@ Reset Columns + (click)="generateCsvExportUrl($event)" + [download]="csvExportFileName" + [href]="csvExportUrl"> cloud_download Download CSV diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.ts index 0e8c74590f..d32fee6db8 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.ts @@ -1,4 +1,5 @@ import {Component, Input, OnInit, Host} from '@angular/core'; +import {DomSanitizer, SafeUrl} from '@angular/platform-browser'; import {Pager} from '@eg/share/util/pager'; import {EgGridColumn, EgGridColumnSet, EgGridToolbarButton, EgGridToolbarAction, EgGridContext, EgGridDataSource} from '@eg/share/grid/grid'; @@ -14,7 +15,11 @@ export class EgGridToolbarComponent implements OnInit { @Input() gridContext: EgGridContext; @Input() colWidthConfig: EgGridColumnWidthComponent; - constructor() {} + csvExportInProgress: boolean; + csvExportUrl: SafeUrl; + csvExportFileName: string; + + constructor(private sanitizer: DomSanitizer) {} ngOnInit() {} @@ -34,9 +39,45 @@ export class EgGridToolbarComponent implements OnInit { action.action(this.gridContext.getSelectedRows()); } - downloadCsv() { - } + generateCsvExportUrl($event) { + + if (this.csvExportInProgress) { + // This is secondary href click handler. Give the + // browser a moment to start the download, then reset + // the CSV download attributes / state. + setTimeout(() => { + this.csvExportUrl = null; + this.csvExportFileName = ''; + this.csvExportInProgress = false; + }, 500 + ); + return; + } + + this.csvExportInProgress = true; + // let the file name describe the grid + this.csvExportFileName = ( + this.gridContext.mainLabel || + this.gridContext.persistKey || + 'eg_grid_data' + ).replace(/\s+/g, '_') + '.csv'; + + this.gridContext.gridToCsv().then(csv => { + console.log(csv); + var blob = new Blob([csv], {type : 'text/plain'}); + let win: any = window; + this.csvExportUrl = this.sanitizer.bypassSecurityTrustUrl( + (win.URL || win.webkitURL).createObjectURL(blob) + ); + + // Fire the 2nd click event now that the browser has + // information on how to download the CSV file. + setTimeout(() => $event.target.click()); + }); + + $event.preventDefault(); + } } diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts index 624b284d68..f2949b380d 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts @@ -17,6 +17,7 @@ import {EgGridContext, EgGridColumn, EgGridDataSource} from './grid'; export class EgGridComponent implements OnInit, AfterViewInit, OnDestroy { + @Input() mainLabel: string; @Input() dataSource: EgGridDataSource; @Input() idlClass: string; @Input() isSortable: boolean; @@ -41,6 +42,7 @@ export class EgGridComponent implements OnInit, AfterViewInit, OnDestroy { } ngOnInit() { + this.context.mainLabel = this.mainLabel; this.context.idlClass = this.idlClass; this.context.dataSource = this.dataSource; this.context.persistKey = this.persistKey diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.ts index 4d1e18d9af..3a5c317112 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts @@ -10,6 +10,8 @@ import {EgStoreService} from '@eg/core/store.service'; import {EgFormatService} from '@eg/share/util/format.service'; import {Pager} from '@eg/share/util/pager'; +const MAX_ALL_ROW_COUNT = 10000; + export class EgGridContext { pager: Pager; @@ -25,6 +27,7 @@ export class EgGridContext { toolbarActions: EgGridToolbarAction[]; lastSelectedIndex: any; pageChanges: Subscription; + mainLabel: string; // Services injected by our grid component idl: EgIdlService; @@ -130,6 +133,14 @@ export class EgGridContext { return this.format.transform({value: val, datatype: col.datatype}); } + getColumnTextContent(row: any, col: EgGridColumn): string { + if (col.cellTemplate) { + // TODO + } else { + return this.getRowColumnValue(row, col); + } + } + selectOneRow(index: any) { this.rowSelector.clear(); this.rowSelector.select(index); @@ -199,6 +210,75 @@ export class EgGridContext { return this.dataSource.requestPage(this.pager); } + getAllRows(): Promise { + let pager = new Pager(); + pager.offset = 0; + pager.limit = MAX_ALL_ROW_COUNT; + return this.dataSource.requestPage(pager); + } + + // Returns a key/value pair object of visible column data as text. + getRowAsFlatText(row: any): any { + let flatRow = {}; + this.columnSet.displayColumns().forEach(col => { + flatRow[col.name] = + this.getColumnTextContent(row, col); + }); + return flatRow; + } + + getAllRowsAsText(): Observable { + return Observable.create(observer => { + this.getAllRows().then(ok => { + this.dataSource.data.forEach(row => { + observer.next(this.getRowAsFlatText(row)); + }) + observer.complete(); + }); + }); + } + + gridToCsv(): Promise { + + let csvStr = ''; + let columns = this.columnSet.displayColumns(); + + // CSV header + columns.forEach(col => { + csvStr += this.valueToCsv(col.label), + csvStr += ','; + }); + + csvStr = csvStr.replace(/,$/,'\n'); + + return new Promise(resolve => { + this.getAllRowsAsText().subscribe( + row => { + columns.forEach(col => { + csvStr += this.valueToCsv(row[col.name]); + csvStr += ','; + }); + csvStr = csvStr.replace(/,$/,'\n'); + }, + err => {}, + () => resolve(csvStr) + ); + }); + } + + + // prepares a string for inclusion within a CSV document + // by escaping commas and quotes and removing newlines. + valueToCsv(str: string): string { + str = ''+str; + if (!str) return ''; + str = str.replace(/\n/g, ''); + if (str.match(/\,/) || str.match(/"/)) { + str = str.replace(/"/g, '""'); + str = '"' + str + '"'; + } + return str; + } generateColumns() { @@ -510,8 +590,7 @@ export class EgGridDataSource { requestPage(pager: Pager): Promise { if ( - // already have the current page - this.getPageOfRows(pager).length > 0 + this.getPageOfRows(pager).length == pager.limit // already have all data || this.allRowsRetrieved // have no way to get more data.