Skip to content

Commit

Permalink
feat(export): add Export to Excel feature
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding-SE committed Oct 11, 2019
1 parent 1665f34 commit f06977f
Show file tree
Hide file tree
Showing 22 changed files with 590 additions and 45 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
"custom-event-polyfill": "^1.0.7",
"del": "^3.0.0",
"del-cli": "^1.1.0",
"excel-builder-webpack": "^1.0.3",
"gulp": "^4.0.2",
"gulp-bump": "^3.1.3",
"gulp-sass": "^4.0.2",
Expand All @@ -133,6 +134,8 @@
"jest-extended": "^0.11.2",
"jest-junit": "^6.4.0",
"jest-preset-angular": "^6.0.1",
"jszip": "^3.2.2",
"lodash": "^4.17.15",
"ng-packagr": "~5.3.0",
"ngx-bootstrap": "^4.3.0",
"node-sass": "^4.12.0",
Expand Down
70 changes: 38 additions & 32 deletions src/app/examples/grid-localization.component.html
Original file line number Diff line number Diff line change
@@ -1,38 +1,44 @@
<div class="container-fluid">
<h2>{{title}}</h2>
<div class="subtitle" [innerHTML]="subTitle"></div>
<h2>{{title}}</h2>
<div class="subtitle" [innerHTML]="subTitle"></div>

<hr/>
<hr />

<div class="row col-sm-12">
<button class="btn btn-default btn-sm" (click)="switchLanguage()">
<i class="fa fa-language"></i>
Switch Language
</button>
<b>Locale:</b> <span style="font-style: italic">{{selectedLanguage + '.json'}}</span>
<div class="row col-sm-12">
<button class="btn btn-default btn-sm" (click)="switchLanguage()">
<i class="fa fa-language"></i>
Switch Language
</button>
<b>Locale:</b> <span style="font-style: italic">{{selectedLanguage + '.json'}}</span>

<span style="margin-left: 20px">
<button class="btn btn-default btn-sm" (click)="exportToFile('csv')">
<i class="fa fa-download"></i>
Download to CSV
</button>
<button class="btn btn-default btn-sm" (click)="exportToFile('txt')">
<i class="fa fa-download"></i>
Download to Text
</button>
<button class="btn btn-default btn-sm" (click)="dynamicallyAddTitleHeader()">
<i class="fa fa-plus"></i>
Dynamically Duplicate Title Column
</button>
</span>
</div>
<span style="margin-left: 20px">
<button class="btn btn-default btn-sm" (click)="exportToFile('csv')">
<i class="fa fa-download"></i>
Download to CSV
</button>
<button class="btn btn-default btn-sm" (click)="exportToFile('txt')">
<i class="fa fa-download"></i>
Download to Text
</button>
<button class="btn btn-default btn-sm" (click)="exportToExcel()">
<i class="fa fa-file-excel-o text-success"></i>
Download to Excel
</button>
</span>
<span style="margin-left: 10px">
<button class="btn btn-default btn-sm" (click)="dynamicallyAddTitleHeader()">
<i class="fa fa-plus"></i>
Dynamically Duplicate Title Column
</button>
</span>
</div>

<div class="col-sm-12">
<angular-slickgrid gridId="grid12"
(onAngularGridCreated)="angularGridReady($event)"
[columnDefinitions]="columnDefinitions"
[gridOptions]="gridOptions"
[dataset]="dataset">
</angular-slickgrid>
</div>
<div class="col-sm-12">
<angular-slickgrid gridId="grid12"
(onAngularGridCreated)="angularGridReady($event)"
[columnDefinitions]="columnDefinitions"
[gridOptions]="gridOptions"
[dataset]="dataset">
</angular-slickgrid>
</div>
</div>
11 changes: 11 additions & 0 deletions src/app/examples/grid-localization.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ export class GridLocalizationComponent implements OnInit {
{
id: 'duration', name: 'Duration (days)', field: 'duration', headerKey: 'DURATION', sortable: true,
formatter: Formatters.percentCompleteBar, minWidth: 100,
exportWithFormatter: false,
filterable: true,
type: FieldType.number,
filter: { model: Filters.slider, /* operator: '>=',*/ params: { hideSliderNumber: true } }
},
{ id: 'start', name: 'Start', field: 'start', headerKey: 'START', formatter: Formatters.dateIso, outputType: FieldType.dateIso, type: FieldType.date, minWidth: 100, filterable: true, filter: { model: Filters.compoundDate } },
Expand Down Expand Up @@ -123,6 +125,8 @@ export class GridLocalizationComponent implements OnInit {
},
enableAutoResize: true,
enableExcelCopyBuffer: true,
enableExcelExport: true,
enableExport: true,
enableFiltering: true,
enableTranslate: true,
i18n: this.translate,
Expand Down Expand Up @@ -170,6 +174,13 @@ export class GridLocalizationComponent implements OnInit {
this.columnDefinitions = this.columnDefinitions.slice();
}

exportToExcel() {
this.angularGrid.excelExportService.exportToExcel({
filename: 'Export',
format: FileType.xlsx
});
}

exportToFile(type = 'csv') {
this.angularGrid.exportService.exportToFile({
delimiter: (type === 'csv') ? DelimiterType.comma : DelimiterType.tab,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SlickPaginationComponent } from './slick-pagination.component';
import { CollectionService } from '../services/collection.service';
import {
AngularUtilService,
ExcelExportService,
ExportService,
ExtensionService,
FilterService,
Expand Down Expand Up @@ -74,6 +75,7 @@ describe('App Component', () => {
providers: [
AngularUtilService,
CollectionService,
ExcelExportService,
ExportService,
ExtensionService,
ExtensionUtility,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { isObservable, Observable, Subscription } from 'rxjs';

// Services
import { AngularUtilService } from '../services/angularUtil.service';
import { ExcelExportService } from '../services/excelExport.service';
import { ExportService } from './../services/export.service';
import { ExtensionService } from '../services/extension.service';
import { ExtensionUtility } from '../extensions/extensionUtility';
Expand Down Expand Up @@ -79,6 +80,7 @@ const slickgridEventPrefix = 'sg';
ColumnPickerExtension,
DraggableGroupingExtension,
ExtensionService,
ExcelExportService,
ExportService,
ExtensionUtility,
FilterFactory,
Expand Down Expand Up @@ -165,6 +167,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn

constructor(
private elm: ElementRef,
private excelExportService: ExcelExportService,
private exportService: ExportService,
private extensionService: ExtensionService,
private extensionUtility: ExtensionUtility,
Expand Down Expand Up @@ -330,6 +333,11 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
this.exportService.init(this.grid, this.dataView);
}

// if Excel Export is enabled, initialize the service with the necessary grid and other objects
if (this.gridOptions.enableExcelExport) {
this.excelExportService.init(this.grid, this.dataView);
}

// once all hooks are in placed and the grid is initialized, we can emit an event
this.onGridInitialized.emit(this.grid);

Expand All @@ -351,6 +359,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn

// return all available Services (non-singleton)
backendService: this.gridOptions && this.gridOptions.backendServiceApi && this.gridOptions.backendServiceApi.service,
excelExportService: this.excelExportService,
exportService: this.exportService,
extensionService: this.extensionService,
filterService: this.filterService,
Expand Down
1 change: 1 addition & 0 deletions src/app/modules/angular-slickgrid/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class Constants {
TEXT_ENDS_WITH: 'Ends With',
TEXT_EXPORT_IN_CSV_FORMAT: 'Export in CSV format',
TEXT_EXPORT_IN_TEXT_FORMAT: 'Export in Text format (Tab delimited)',
TEXT_EXPORT_TO_EXCEL: 'Export to Excel',
TEXT_FORCE_FIT_COLUMNS: 'Force fit columns',
TEXT_GROUP_BY: 'Group By',
TEXT_HIDE_COLUMN: 'Hide Column',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { TranslateService, TranslateModule } from '@ngx-translate/core';

import { GridOption } from '../../models/gridOption.interface';
import { GridMenuExtension } from '../gridMenuExtension';
import { ExtensionUtility } from '../extensionUtility';
import { SharedService } from '../../services/shared.service';
import { Column, DelimiterType, FileType } from '../../models';
import { ExportService, FilterService, SortService } from '../../services';
import { ExcelExportService, ExportService, FilterService, SortService } from '../../services';

declare var Slick: any;

Expand Down Expand Up @@ -127,6 +128,7 @@ describe('gridMenuExtension', () => {
CLEAR_ALL_FILTERS: 'Supprimer tous les filtres',
CLEAR_ALL_SORTING: 'Supprimer tous les tris',
EXPORT_TO_CSV: 'Exporter en format CSV',
EXPORT_TO_EXCEL: 'Exporter vers Excel',
EXPORT_TO_TAB_DELIMITED: 'Exporter en format texte (délimité par tabulation)',
HELP: 'Aide',
COMMANDS: 'Commandes',
Expand Down Expand Up @@ -396,7 +398,7 @@ describe('gridMenuExtension', () => {
});

it('should have the "export-csv" menu command when "enableExport" is set', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportTextDelimitedCommand: true } } as unknown as GridOption;
const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportExcelCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();
extension.register(); // calling 2x register to make sure it doesn't duplicate commands
Expand All @@ -406,14 +408,14 @@ describe('gridMenuExtension', () => {
});

it('should not have the "export-csv" menu command when "enableExport" and "hideExportCsvCommand" are set', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportCsvCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption;
const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportExcelCommand: true, hideExportCsvCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();
expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]);
});

it('should have the "export-text-delimited" menu command when "enableExport" is set', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportCsvCommand: true } } as unknown as GridOption;
const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportCsvCommand: true, hideExportExcelCommand: true } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();
extension.register(); // calling 2x register to make sure it doesn't duplicate commands
Expand All @@ -423,7 +425,7 @@ describe('gridMenuExtension', () => {
});

it('should not have the "export-text-delimited" menu command when "enableExport" and "hideExportCsvCommand" are set', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportCsvCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption;
const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportExcelCommand: true, hideExportCsvCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();
expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]);
Expand Down Expand Up @@ -471,6 +473,7 @@ describe('gridMenuExtension', () => {
extension.register();
expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([
{ command: 'export-csv', disabled: false, iconCssClass: 'fa fa-download', positionOrder: 53, title: 'Exporter en format CSV' },
{ command: 'export-excel', disabled: false, iconCssClass: 'fa fa-file-excel-o text-success', positionOrder: 53, title: 'Exporter vers Excel' },
{ command: 'help', disabled: false, iconCssClass: 'fa fa-question-circle', positionOrder: 99, title: 'Aide', titleKey: 'HELP' },
]);
});
Expand All @@ -480,6 +483,7 @@ describe('gridMenuExtension', () => {
extension.register();
expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([
{ command: 'export-csv', disabled: false, iconCssClass: 'fa fa-download', positionOrder: 53, title: 'Exporter en format CSV' },
{ command: 'export-excel', disabled: false, iconCssClass: 'fa fa-file-excel-o text-success', positionOrder: 53, title: 'Exporter vers Excel' },
{ command: 'help', disabled: false, iconCssClass: 'fa fa-question-circle', positionOrder: 99, title: 'Aide', titleKey: 'HELP' },
]);
});
Expand Down Expand Up @@ -680,7 +684,7 @@ describe('gridMenuExtension', () => {
describe('without ngx-translate', () => {
beforeEach(() => {
translate = null;
extension = new GridMenuExtension({} as ExportService, {} as ExtensionUtility, {} as FilterService, { gridOptions: { enableTranslate: true } } as SharedService, {} as SortService, translate);
extension = new GridMenuExtension({} as ExportService, {} as ExtensionUtility, {} as FilterService, { gridOptions: { enableTranslate: true } } as SharedService, {} as SortService, {} as ExcelExportService, translate);
});

it('should throw an error if "enableTranslate" is set but the I18N Service is null', () => {
Expand Down
30 changes: 27 additions & 3 deletions src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
Locale,
SlickEventHandler,
} from '../models';
import { ExcelExportService } from '../services/excelExport.service';
import { ExportService } from '../services/export.service';
import { ExtensionUtility } from './extensionUtility';
import { FilterService } from '../services/filter.service';
Expand All @@ -39,6 +40,7 @@ export class GridMenuExtension implements Extension {
private filterService: FilterService,
private sharedService: SharedService,
private sortService: SortService,
@Optional() private excelExportService: ExcelExportService,
@Optional() private translate: TranslateService,
) {
this._eventHandler = new Slick.EventHandler();
Expand Down Expand Up @@ -320,7 +322,7 @@ export class GridMenuExtension implements Extension {
}
}

// show grid menu: export to file
// show grid menu: Export to file
if (this.sharedService.gridOptions && this.sharedService.gridOptions.enableExport && this.sharedService.gridOptions.gridMenu && !this.sharedService.gridOptions.gridMenu.hideExportCsvCommand) {
const commandName = 'export-csv';
if (!originalCustomItems.find((item) => item.command === commandName)) {
Expand All @@ -336,6 +338,22 @@ export class GridMenuExtension implements Extension {
}
}

// show grid menu: Export to Excel
if (this.sharedService.gridOptions && this.sharedService.gridOptions.enableExport && this.sharedService.gridOptions.gridMenu && !this.sharedService.gridOptions.gridMenu.hideExportExcelCommand) {
const commandName = 'export-excel';
if (!originalCustomItems.find((item) => item.command === commandName)) {
gridMenuCustomItems.push(
{
iconCssClass: this.sharedService.gridOptions.gridMenu.iconExportExcelCommand || 'fa fa-file-excel-o text-success',
title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant('EXPORT_TO_EXCEL') : this._locales && this._locales.TEXT_EXPORT_TO_EXCEL,
disabled: false,
command: commandName,
positionOrder: 53
}
);
}
}

// show grid menu: export to text file as tab delimited
if (this.sharedService.gridOptions && this.sharedService.gridOptions.enableExport && this.sharedService.gridOptions.gridMenu && !this.sharedService.gridOptions.gridMenu.hideExportTextDelimitedCommand) {
const commandName = 'export-text-delimited';
Expand Down Expand Up @@ -382,15 +400,21 @@ export class GridMenuExtension implements Extension {
delimiter: DelimiterType.comma,
filename: 'export',
format: FileType.csv,
useUtf8WithBom: true
useUtf8WithBom: true,
});
break;
case 'export-excel':
this.excelExportService.exportToExcel({
filename: 'export',
format: FileType.xlsx,
});
break;
case 'export-text-delimited':
this.exportService.exportToFile({
delimiter: DelimiterType.tab,
filename: 'export',
format: FileType.txt,
useUtf8WithBom: true
useUtf8WithBom: true,
});
break;
case 'toggle-filter':
Expand Down
14 changes: 13 additions & 1 deletion src/app/modules/angular-slickgrid/global-grid-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,23 @@ export const GlobalGridOptions: GridOption = {
enableCellNavigation: false,
enableColumnPicker: true,
enableColumnReorder: true,
enableExport: true,
enableExcelExport: false,
enableExport: true, // CSV by default (could export to CSV)
enableGridMenu: true,
enableHeaderMenu: true,
enableMouseHoverHighlightRow: true,
enableSorting: true,
enableTextSelectionOnCells: true,
explicitInitialization: true,
excelExportOptions: {
exportWithFormatter: false,
filename: 'export',
format: FileType.xlsx,
groupingColumnHeaderTitle: 'Group By',
groupingAggregatorRowText: '',
sanitizeDataExport: false,
useUtf8WithBom: true
},
exportOptions: {
delimiter: DelimiterType.comma,
exportWithFormatter: false,
Expand All @@ -57,6 +67,7 @@ export const GlobalGridOptions: GridOption = {
hideClearAllFiltersCommand: false,
hideClearAllSortingCommand: false,
hideExportCsvCommand: false,
hideExportExcelCommand: true,
hideExportTextDelimitedCommand: true,
hideForceFitButton: false,
hideRefreshDatasetCommand: false,
Expand All @@ -67,6 +78,7 @@ export const GlobalGridOptions: GridOption = {
iconClearAllFiltersCommand: 'fa fa-filter text-danger',
iconClearAllSortingCommand: 'fa fa-unsorted text-danger',
iconExportCsvCommand: 'fa fa-download',
iconExportExcelCommand: 'fa fa-file-excel-o text-success',
iconExportTextDelimitedCommand: 'fa fa-download',
iconRefreshDatasetCommand: 'fa fa-refresh',
iconToggleFilterCommand: 'fa fa-random',
Expand Down
Loading

0 comments on commit f06977f

Please sign in to comment.