Skip to content

Commit

Permalink
feat(GridState and Presets) (#23)
Browse files Browse the repository at this point in the history
* feat(grid): Grid State and Grid Presets feature

* fix(grid): avoid errors by making sure the dataview exist

* fix(events): avoid subscribe to run even after dispose by re-creating it

* fix(state): Grid State output should have properties of it's own
  • Loading branch information
ghiscoding authored Mar 4, 2018
1 parent 72d8d6b commit 78c32e5
Show file tree
Hide file tree
Showing 41 changed files with 1,246 additions and 423 deletions.
107 changes: 73 additions & 34 deletions aurelia-slickgrid/src/aurelia-slickgrid/aurelia-slickgrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
GraphqlService,
GridEventService,
GridExtraService,
GridStateService,
ResizerService,
SortService,
toKebabCase
Expand All @@ -51,7 +52,7 @@ declare var Slick: any;
const eventPrefix = 'sg';

// Aurelia doesn't support well TypeScript @autoinject in a Plugin so we'll do it the old fashion way
@inject(ControlAndPluginService, ExportService, Element, EventAggregator, FilterService, GraphqlService, GridEventService, GridExtraService, I18N, ResizerService, SortService)
@inject(ControlAndPluginService, ExportService, Element, EventAggregator, FilterService, GraphqlService, GridEventService, GridExtraService, GridStateService, I18N, ResizerService, SortService)
export class AureliaSlickgridCustomElement {
private _dataset: any[];
private _gridOptions: GridOption;
Expand Down Expand Up @@ -84,6 +85,7 @@ export class AureliaSlickgridCustomElement {
private graphqlService: GraphqlService,
private gridEventService: GridEventService,
private gridExtraService: GridExtraService,
private gridStateService: GridStateService,
private i18n: I18N,
private resizer: ResizerService,
private sortService: SortService) { }
Expand Down Expand Up @@ -138,6 +140,14 @@ export class AureliaSlickgridCustomElement {
if (this._gridOptions.enableExport) {
this.exportService.init(this.grid, this._gridOptions, this.dataview);
}

// attach the Backend Service API callback functions only after the grid is initialized
// because the preProcess() and onInit() might get triggered
if (this._gridOptions && (this._gridOptions.backendServiceApi || this._gridOptions.onBackendEventApi)) {
this.attachBackendCallbackFunctions(this._gridOptions);
}

this.gridStateService.init(this.grid, this.filterService, this.sortService);
}

detached() {
Expand Down Expand Up @@ -223,12 +233,17 @@ export class AureliaSlickgridCustomElement {

// attach external sorting (backend) when available or default onSort (dataView)
if (gridOptions.enableSorting) {
(gridOptions.backendServiceApi || gridOptions.onBackendEventApi) ? this.sortService.attachBackendOnSort(grid, gridOptions) : this.sortService.attachLocalOnSort(grid, gridOptions, this.dataview);
(gridOptions.backendServiceApi || gridOptions.onBackendEventApi) ? this.sortService.attachBackendOnSort(grid, gridOptions) : this.sortService.attachLocalOnSort(grid, gridOptions, this.dataview, this.columnDefinitions);
}

// attach external filter (backend) when available or default onFilter (dataView)
if (gridOptions.enableFiltering) {
this.filterService.init(grid, gridOptions, this.columnDefinitions);

// if user entered some "presets", we need to reflect them all in the DOM
if (gridOptions.presets && gridOptions.presets.filters) {
this.filterService.populateColumnFilterSearchTerms(gridOptions, this.columnDefinitions);
}
(gridOptions.backendServiceApi || gridOptions.onBackendEventApi) ? this.filterService.attachBackendOnFilter(grid, gridOptions) : this.filterService.attachLocalOnFilter(grid, gridOptions, this.dataview);
}

Expand All @@ -239,37 +254,7 @@ export class AureliaSlickgridCustomElement {
}

if (gridOptions.backendServiceApi && gridOptions.backendServiceApi.service) {
gridOptions.backendServiceApi.service.initOptions(gridOptions.backendServiceApi.options || {}, gridOptions.pagination);
}

const backendApi = gridOptions.backendServiceApi || gridOptions.onBackendEventApi;
const serviceOptions: BackendServiceOption = (backendApi && backendApi.service && backendApi.service.options) ? backendApi.service.options : {};
const isExecuteCommandOnInit = (!serviceOptions) ? false : ((serviceOptions && serviceOptions.hasOwnProperty('executeProcessCommandOnInit')) ? serviceOptions['executeProcessCommandOnInit'] : true);

if (backendApi && backendApi.service && (backendApi.onInit || isExecuteCommandOnInit)) {
const query = (typeof backendApi.service.buildQuery === 'function') ? backendApi.service.buildQuery() : '';
const onInitPromise = (isExecuteCommandOnInit) ? (backendApi && backendApi.process) ? backendApi.process(query) : undefined : (backendApi && backendApi.onInit) ? backendApi.onInit(query) : null;

// wrap this inside a setTimeout to avoid timing issue since the gridOptions needs to be ready before running this onInit
setTimeout(async () => {
if (backendApi.preProcess) {
backendApi.preProcess();
}

// await for the Promise to resolve the data
const processResult = await onInitPromise;

// define what our internal Post Process callback, only available for GraphQL Service for now
// it will basically refresh the Dataset & Pagination without having the user to create his own PostProcess every time
if (processResult && backendApi && backendApi.service instanceof GraphqlService && backendApi.internalPostProcess) {
backendApi.internalPostProcess(processResult);
}

// send the response process to the postProcess callback
if (backendApi.postProcess) {
backendApi.postProcess(processResult);
}
});
gridOptions.backendServiceApi.service.init(gridOptions.backendServiceApi.options || {}, gridOptions.pagination, this.grid);
}
}

Expand Down Expand Up @@ -315,6 +300,56 @@ export class AureliaSlickgridCustomElement {
});
}

attachBackendCallbackFunctions(gridOptions: GridOption) {
const backendApi = gridOptions.backendServiceApi || gridOptions.onBackendEventApi;
const serviceOptions: BackendServiceOption = (backendApi && backendApi.service && backendApi.service.options) ? backendApi.service.options : {};
const isExecuteCommandOnInit = (!serviceOptions) ? false : ((serviceOptions && serviceOptions.hasOwnProperty('executeProcessCommandOnInit')) ? serviceOptions['executeProcessCommandOnInit'] : true);

// update backend filters (if need be) before the query runs
if (gridOptions && gridOptions.presets) {
if (gridOptions.presets.filters) {
backendApi.service.updateFilters(gridOptions.presets.filters, true);
}
if (gridOptions.presets.sorters) {
backendApi.service.updateSorters(null, gridOptions.presets.sorters);
}
if (gridOptions.presets.pagination) {
backendApi.service.updatePagination(gridOptions.presets.pagination.pageNumber, gridOptions.presets.pagination.pageSize);
}
} else {
const columnFilters = this.filterService.getColumnFilters();
if (columnFilters) {
backendApi.service.updateFilters(columnFilters, false);
}
}

if (backendApi && backendApi.service && (backendApi.onInit || isExecuteCommandOnInit)) {
const query = (typeof backendApi.service.buildQuery === 'function') ? backendApi.service.buildQuery() : '';
const onInitPromise = (isExecuteCommandOnInit) ? (backendApi && backendApi.process) ? backendApi.process(query) : undefined : (backendApi && backendApi.onInit) ? backendApi.onInit(query) : null;

// wrap this inside a setTimeout to avoid timing issue since the gridOptions needs to be ready before running this onInit
setTimeout(async () => {
if (backendApi.preProcess) {
backendApi.preProcess();
}

// await for the Promise to resolve the data
const processResult = await onInitPromise;

// define what our internal Post Process callback, only available for GraphQL Service for now
// it will basically refresh the Dataset & Pagination without having the user to create his own PostProcess every time
if (processResult && backendApi && backendApi.service instanceof GraphqlService && backendApi.internalPostProcess) {
backendApi.internalPostProcess(processResult);
}

// send the response process to the postProcess callback
if (backendApi.postProcess) {
backendApi.postProcess(processResult);
}
});
}
}

attachResizeHook(grid: any, options: GridOption) {
// expand/autofit columns on first page load
if (grid && options.autoFitColumnsOnFirstLoad && typeof grid.autosizeColumns === 'function') {
Expand Down Expand Up @@ -348,7 +383,7 @@ export class AureliaSlickgridCustomElement {
* @param dataset
*/
refreshGridData(dataset: any[], totalCount?: number) {
if (dataset && this.grid) {
if (dataset && this.grid && this.dataview && typeof this.dataview.setItems === 'function') {
this.dataview.setItems(dataset, this._gridOptions.datasetIdPropertyName);

// this.grid.setData(dataset);
Expand All @@ -368,6 +403,10 @@ export class AureliaSlickgridCustomElement {
if (this.gridOptions.pagination && totalCount) {
this.gridOptions.pagination.totalItems = totalCount;
}
if (this.gridOptions.presets && this.gridOptions.presets.pagination && this.gridOptions.pagination) {
this.gridOptions.pagination.pageSize = this.gridOptions.presets.pagination.pageSize;
this.gridOptions.pagination.pageNumber = this.gridOptions.presets.pagination.pageNumber;
}
this.gridPaginationOptions = this.mergeGridOptions();
}
if (this.grid && this._gridOptions.enableAutoResize) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ export class InputFilter implements Filter {
}
}

/**
* Set value(s) on the DOM element
*/
setValues(values) {
if (values) {
this.$filterElm.val(values);
}
}

//
// private functions
// ------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ export class MultipleSelectFilter implements Filter {
}
}

/**
* Set value(s) on the DOM element
*/
setValues(values) {
if (values) {
this.$filterElm.multipleSelect('setSelects', values);
}
}

//
// private functions
// ------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ export class SelectFilter implements Filter {
}
}

/**
* Set value(s) on the DOM element
*/
setValues(values) {
if (values) {
this.$filterElm.val(values);
}
}

//
// private functions
// ------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ export class SingleSelectFilter implements Filter {
}
}

/**
* Set value(s) on the DOM element
*/
setValues(values) {
if (values) {
values = Array.isArray(values) ? values : [values];
this.$filterElm.multipleSelect('setSelects', values);
}
}

//
// private functions
// ------------------
Expand Down
10 changes: 9 additions & 1 deletion aurelia-slickgrid/src/aurelia-slickgrid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import {
GraphqlServiceOption,
GridOption,
OnEventArgs,
SearchTerm
OperatorType,
SearchTerm,
SortDirection,
SortDirectionString
} from './models/index';

// editors, formatters, ...
Expand All @@ -48,6 +51,7 @@ import {
GridExtraService,
GridEventService,
GridOdataService,
GridStateService,
ResizerService,
SortService
} from './services/index';
Expand Down Expand Up @@ -96,7 +100,10 @@ export {
FormElementType,
FieldType,
OnEventArgs,
OperatorType,
SearchTerm,
SortDirection,
SortDirectionString,

Editors,
Filters,
Expand All @@ -113,6 +120,7 @@ export {
GridExtraUtils,
GridExtraService,
GridOdataService,
GridStateService,
ResizerService,
SortService,

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,72 @@
import { BackendServiceOption } from './backendServiceOption.interface';
import { FilterChangedArgs } from './filterChangedArgs.interface';
import { Pagination } from './pagination.interface';
import { PaginationChangedArgs } from './paginationChangedArgs.interface';
import { SortChangedArgs } from './sortChangedArgs.interface';
import { EventAggregator, Subscription } from 'aurelia-event-aggregator';
import {
BackendServiceOption,
Column,
ColumnFilters,
CurrentFilter,
CurrentPagination,
CurrentSorter,
FilterChangedArgs,
GridOption,
Pagination,
PaginationChangedArgs,
SortChangedArgs,
SortChanged
} from './../models/index';

export interface BackendService {
/** Backend Service options */
options?: BackendServiceOption;

/** Build and the return the backend service query string */
buildQuery: (serviceOptions?: BackendServiceOption) => string;
initOptions: (serviceOptions?: BackendServiceOption, pagination?: Pagination) => void;

/** initialize the backend service with certain options */
init?: (serviceOptions?: BackendServiceOption, pagination?: Pagination, grid?: any) => void;

/** DEPRECATED, please use "init()" instead */
initOptions?: (serviceOptions?: BackendServiceOption, pagination?: Pagination, gridOptions?: GridOption, columnDefinitions?: Column[]) => void;

/** Get the dataset name */
getDatasetName?: () => string;

/** Get the Filters that are currently used by the grid */
getCurrentFilters?: () => ColumnFilters | CurrentFilter[];

/** Get the Pagination that is currently used by the grid */
getCurrentPagination?: () => CurrentPagination;

/** Get the Sorters that are currently used by the grid */
getCurrentSorters?: () => ColumnFilters | CurrentFilter[];

/** Reset the pagination options */
resetPaginationOptions: () => void;

/** Update the Filters options with a set of new options */
updateFilters?: (columnFilters: ColumnFilters | CurrentFilter[], isUpdatedByPreset: boolean) => void;

/** Update the Pagination component with it's new page number and size */
updatePagination?: (newPage: number, pageSize: number) => void;

/** Update the Sorters options with a set of new options */
updateSorters?: (sortColumns?: SortChanged[], presetSorters?: CurrentSorter[]) => void;

/** Update the backend service options */
updateOptions: (serviceOptions?: BackendServiceOption) => void;

// --
// Events / Methods
// -----------------

/** Fired when the pagination needs to be forced refreshed (by a Preset call) */
onPaginationRefreshed?: EventAggregator; // EventEmitter<PaginationChangedArgs>;

/** Execute when any of the filters changed */
onFilterChanged: (event: Event, args: FilterChangedArgs) => Promise<string>;

/** Execute when the pagination changed */
onPaginationChanged: (event: Event | undefined, args: PaginationChangedArgs) => string;

/** Execute when any of the sorters changed */
onSortChanged: (event: Event, args: SortChangedArgs) => string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
FilterType,
FormElementType,
MultipleSelectOption,
OperatorString,
OperatorType,
SearchTerm
} from './../models/index';

Expand All @@ -27,7 +29,7 @@ export interface ColumnFilter {
searchTerms?: SearchTerm[];

/** Operator to use when filtering (>, >=, EQ, IN, ...) */
operator?: string;
operator?: OperatorType | OperatorString;

/** Filter Type to use (input, multipleSelect, singleSelect, select, custom) */
type?: FilterType | FormElementType | string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { OperatorString, OperatorType, SearchTerm } from './../models/index';

export interface CurrentFilter {
columnId: string;
operator?: OperatorType | OperatorString;
searchTerm?: SearchTerm;
searchTerms?: SearchTerm[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface CurrentPagination {
pageNumber: number;
pageSize: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { SortDirection, SortDirectionString } from './../models/index';

export interface CurrentSorter {
columnId: string;
direction: SortDirection | SortDirectionString;
}
Loading

0 comments on commit 78c32e5

Please sign in to comment.