Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(event): #24 add onGridStateChanged and onPaginationChanged events #28

Merged
merged 5 commits into from
Mar 13, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<div id.bind="gridId" class="slickgrid-container" style.bind="style">
</div>

<slick-pagination id="slickPagingContainer-${gridId}" if.bind="showPagination" pagination-options.bind="gridPaginationOptions"></slick-pagination>
<slick-pagination id="slickPagingContainer-${gridId}" if.bind="showPagination" on-pagination-changed.delegate="paginationChanged($event.detail)"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment about using the eventPrefix

pagination-options.bind="gridPaginationOptions"></slick-pagination>
</div>
</template>
27 changes: 25 additions & 2 deletions aurelia-slickgrid/src/aurelia-slickgrid/aurelia-slickgrid.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { GridStateChange } from './models/gridStateChange.interface';
// import 3rd party vendor libs
import 'slickgrid/lib/jquery-ui-1.11.3';
import 'slickgrid/lib/jquery.event.drag-2.3.0';
Expand Down Expand Up @@ -30,7 +31,9 @@ import {
Column,
FormElementType,
GraphqlResult,
GridOption
GridOption,
GridStateType,
Pagination,
} from './models/index';
import {
ControlAndPluginService,
Expand All @@ -56,6 +59,7 @@ const eventPrefix = 'sg';
export class AureliaSlickgridCustomElement {
private _dataset: any[];
private _eventHandler: any = new Slick.EventHandler();
gridStateSubscriber: Subscription;
gridHeightString: string;
gridWidthString: string;
localeChangedSubscriber: Subscription;
Expand Down Expand Up @@ -158,11 +162,13 @@ export class AureliaSlickgridCustomElement {
this.dataview = [];
this._eventHandler.unsubscribeAll();
this.controlAndPluginService.dispose();
this.gridEventService.dispose();
this.filterService.dispose();
this.gridEventService.dispose();
this.gridStateService.dispose();
this.resizer.dispose();
this.sortService.dispose();
this.grid.destroy();
this.gridStateSubscriber.dispose();
this.localeChangedSubscriber.dispose();
this.ea.publish('onAfterGridDestroyed', true);
this.elm.dispatchEvent(new CustomEvent(`${eventPrefix}-on-after-grid-destroyed`, {
Expand Down Expand Up @@ -258,6 +264,7 @@ export class AureliaSlickgridCustomElement {
}
}

// expose all Slick Grid Events through dispatch
for (const prop in grid) {
if (grid.hasOwnProperty(prop) && prop.startsWith('on')) {
this._eventHandler.subscribe(grid[prop], (e: any, args: any) => {
Expand All @@ -272,6 +279,7 @@ export class AureliaSlickgridCustomElement {
}
}

// expose all Slick DataView Events through dispatch
for (const prop in dataView) {
if (dataView.hasOwnProperty(prop) && prop.startsWith('on')) {
this._eventHandler.subscribe(dataView[prop], (e: any, args: any) => {
Expand All @@ -286,6 +294,14 @@ export class AureliaSlickgridCustomElement {
}
}

// expose GridState Service changes event through dispatch
this.gridStateSubscriber = this.ea.subscribe('gridStateService:changed', (gridStateChange: GridStateChange) => {
this.elm.dispatchEvent(new CustomEvent(`on-grid-state-service-changed`, {
Copy link
Collaborator

@jmzagorski jmzagorski Mar 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about the eventPrefix

bubbles: true,
detail: gridStateChange
}));
});

// on cell click, mainly used with the columnDef.action callback
this.gridEventService.attachOnCellChange(grid, this.gridOptions, dataView);
this.gridEventService.attachOnClick(grid, this.gridOptions, dataView);
Expand Down Expand Up @@ -381,6 +397,13 @@ export class AureliaSlickgridCustomElement {
return $.extend(true, {}, GlobalGridOptions, this.gridOptions);
}

paginationChanged(pagination: Pagination) {
this.ea.publish('gridStateService:changed', {
change: { newValues: pagination, type: GridStateType.pagination },
gridState: this.gridStateService.getCurrentGridState()
});
}

/**
* When dataset changes, we need to refresh the entire grid UI & possibly resize it as well
* @param dataset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const collectionFormatter: Formatter = (row: number, cell: number, value:
value.map((v: any) => collection.findOrDefault((c: any) => c[valueName] === v)[labelName]),
columnDef,
dataContext);
}
}

return collection.findOrDefault((c: any) => c[valueName] === value)[labelName] || '';
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { CurrentSorter } from './currentSorter.interface';
import { CurrentFilter } from './currentFilter.interface';
import { CurrentFilter, CurrentSorter } from './../models/index';

export interface GridState {
/** Filters (and their state, columnId, searchTerm(s)) that are currently applied in the grid */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CurrentFilter, CurrentSorter, GridState, GridStateType, Pagination } from './../models/index';

export interface GridStateChange {
/** Changes that were triggered */
change?: {
newValues: CurrentFilter[] | CurrentSorter[] | Pagination;
type: GridStateType;
};

/** Current grid state */
gridState?: GridState;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum GridStateType {
filter = 'filter',
pagination = 'pagination',
sorter = 'sorter'
}
4 changes: 3 additions & 1 deletion aurelia-slickgrid/src/aurelia-slickgrid/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ export * from './graphqlPaginationOption.interface';
export * from './graphqlResult.interface';
export * from './graphqlServiceOption.interface';
export * from './graphqlSortingOption.interface';
export * from './gridState.interface';
export * from './gridMenu.interface';
export * from './gridOption.interface';
export * from './gridState.interface';
export * from './gridStateChange.interface';
export * from './gridStateType.enum';
export * from './headerButton.interface';
export * from './headerButtonItem.interface';
export * from './headerButtonOnCommandArgs.interface';
Expand Down
100 changes: 57 additions & 43 deletions aurelia-slickgrid/src/aurelia-slickgrid/services/filter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,18 @@ import * as $ from 'jquery';
// using external non-typed js libraries
declare var Slick: any;

@inject(FilterFactory)
@inject(EventAggregator, FilterFactory)
export class FilterService {
private _eventHandler = new Slick.EventHandler();
private _subscriber: SlickEvent = new Slick.Event();
private _slickSubscriber: SlickEvent;
private _filters: any[] = [];
private _columnFilters: ColumnFilters = {};
private _dataView: any;
private _grid: any;
private _gridOptions: GridOption;
private _onFilterChangedOptions: any;
onFilterChanged = new EventAggregator();

constructor(private filterFactory: FilterFactory) { }
constructor(private ea: EventAggregator, private filterFactory: FilterFactory) { }

init(grid: any, gridOptions: GridOption, columnDefinitions: Column[]): void {
this._grid = grid;
Expand All @@ -47,18 +46,21 @@ export class FilterService {
*/
attachBackendOnFilter(grid: any, options: GridOption) {
this._filters = [];
this.emitFilterChangedBy('remote');
this._slickSubscriber = new Slick.Event();

this._subscriber = new Slick.Event();
this._subscriber.subscribe(this.attachBackendOnFilterSubscribe);
// subscribe to the SlickGrid event and call the backend execution
// we also need to add our filter service (with .bind) to the callback function (which is outside of this service)
// the callback doesn't have access to this service, so we need to bind it
const self = this;
this._slickSubscriber.subscribe(this.attachBackendOnFilterSubscribe.bind(this, self));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since you are binding this to the scope of attachBackendOnFilterSubscirbe you do not need const self = this

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The this that is in there seems to be in fact the SlickGrid event and args, which is why I added the self. I haven't tried (this, this) though but that would look funny

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You only need this._slickSubscriber.subscribe(this.attachBackendOnFilterSubscribe.bind(this));. Then your attachBackendOnFilterSubscribe will have the context of your service already, so using this within the method will work.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok will give that a try a bit later

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you were right about this, I didn't know that simply doing the .bind(this) was enough. Good to know and it cleans up the code a lot. Thanks :)


// subscribe to SlickGrid onHeaderRowCellRendered event to create filter template
this._eventHandler.subscribe(grid.onHeaderRowCellRendered, (e: Event, args: any) => {
this.addFilterTemplateToHeaderRow(args);
});
}

async attachBackendOnFilterSubscribe(event: Event, args: any) {
async attachBackendOnFilterSubscribe(self: FilterService, event: Event, args: any) {
if (!args || !args.grid) {
throw new Error('Something went wrong when trying to attach the "attachBackendOnFilterSubscribe(event, args)" function, it seems that "args" is not populated correctly');
}
Expand All @@ -77,6 +79,9 @@ export class FilterService {
// call the service to get a query back
const query = await backendApi.service.onFilterChanged(event, args);

// emit an onFilterChanged event
self.emitFilterChanged('remote');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when self is removed this can be this.emitFilterChanged('remote')


// await for the Promise to resolve the data
const processResult = await backendApi.process(query);

Expand All @@ -91,6 +96,34 @@ export class FilterService {
}
}

/**
* Attach a local filter hook to the grid
* @param grid SlickGrid Grid object
* @param gridOptions Grid Options object
* @param dataView
*/
attachLocalOnFilter(grid: any, options: GridOption, dataView: any) {
this._filters = [];
this._dataView = dataView;
this._slickSubscriber = new Slick.Event();

dataView.setFilterArgs({ columnFilters: this._columnFilters, grid: this._grid });
dataView.setFilter(this.customLocalFilter.bind(this, dataView));

this._slickSubscriber.subscribe((e: any, args: any) => {
const columnId = args.columnId;
if (columnId != null) {
dataView.refresh();
}
this.emitFilterChanged('local');
});

// subscribe to SlickGrid onHeaderRowCellRendered event to create filter template
this._eventHandler.subscribe(grid.onHeaderRowCellRendered, (e: Event, args: any) => {
this.addFilterTemplateToHeaderRow(args);
});
}

/** Clear the search filters (below the column titles) */
clearFilters() {
this._filters.forEach((filter, index) => {
Expand All @@ -116,34 +149,6 @@ export class FilterService {
}
}

/**
* Attach a local filter hook to the grid
* @param grid SlickGrid Grid object
* @param gridOptions Grid Options object
* @param dataView
*/
attachLocalOnFilter(grid: any, options: GridOption, dataView: any) {
this._dataView = dataView;
this._filters = [];
this.emitFilterChangedBy('local');

dataView.setFilterArgs({ columnFilters: this._columnFilters, grid: this._grid });
dataView.setFilter(this.customLocalFilter.bind(this, dataView));

this._subscriber = new Slick.Event();
this._subscriber.subscribe((e: any, args: any) => {
const columnId = args.columnId;
if (columnId != null) {
dataView.refresh();
}
});

// subscribe to SlickGrid onHeaderRowCellRendered event to create filter template
this._eventHandler.subscribe(grid.onHeaderRowCellRendered, (e: Event, args: any) => {
this.addFilterTemplateToHeaderRow(args);
});
}

customLocalFilter(dataView: any, item: any, args: any) {
for (const columnId of Object.keys(args.columnFilters)) {
const columnFilter = args.columnFilters[columnId];
Expand Down Expand Up @@ -236,8 +241,8 @@ export class FilterService {
this._eventHandler.unsubscribeAll();

// unsubscribe local event
if (this._subscriber && typeof this._subscriber.unsubscribe === 'function') {
this._subscriber.unsubscribe();
if (this._slickSubscriber && typeof this._slickSubscriber.unsubscribe === 'function') {
this._slickSubscriber.unsubscribe();
}
}

Expand Down Expand Up @@ -302,7 +307,7 @@ export class FilterService {
};
}

this.triggerEvent(this._subscriber, {
this.triggerEvent(this._slickSubscriber, {
columnId,
columnDef: args.columnDef || null,
columnFilters: this._columnFilters,
Expand Down Expand Up @@ -378,12 +383,21 @@ export class FilterService {
}

/**
* A simple function that is attached to the subscriber and emit a change when the sort is called.
* A simple function that will be called to emit a change when a filter changes.
* Other services, like Pagination, can then subscribe to it.
* @param {string} sender
* @param sender
*/
emitFilterChangedBy(sender: string) {
this._subscriber.subscribe(() => this.onFilterChanged.publish('filterService:changed', `onFilterChanged by ${sender}`));
emitFilterChanged(sender: 'local' | 'remote') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the sender can only be local or remote then you may be able to simplify the this logic. Or are you playing it safe? The simplification can be:

  let currentFilters: CurrentFilter[] = [];

  if (sender === 'remote') {
      const backendService = this._gridOptions.backendServiceApi.service;
      if (backendService && backendService.getCurrentFilters) {
        currentFilters = backendService.getCurrentFilters() as CurrentFilter[];
      }
    } else {
      currentFilters = this.getCurrentLocalFilters();
    }

  this.ea.publish('filterService:filterChanged', currentFilters);

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm playing it safe and it's still better to see what is acceptable right from the argument

if (sender === 'remote') {
let currentFilters: CurrentFilter[] = [];
const backendService = this._gridOptions.backendServiceApi.service;
if (backendService && backendService.getCurrentFilters) {
currentFilters = backendService.getCurrentFilters() as CurrentFilter[];
}
this.ea.publish('filterService:filterChanged', currentFilters);
} else if (sender === 'local') {
this.ea.publish('filterService:filterChanged', this.getCurrentLocalFilters());
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,6 @@ String.titleCase = (inputStr: string): string => {
* @param any [defaultVal={}] the default value to return
* @return object the found object or deafult value
*/
Array.prototype.findOrDefault = function(logic: (item: any) => boolean, defaultVal = {}): any {
Array.prototype.findOrDefault = function (logic: (item: any) => boolean, defaultVal = {}): any {
return this.find(logic) || defaultVal;
};
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { inject } from 'aurelia-framework';
import {
CurrentFilter,
CurrentPagination,
CurrentSorter,
GridOption,
GridState
GridState,
GridStateType,
} from './../models/index';
import { FilterService, SortService } from './../services/index';
import { EventAggregator, Subscription } from 'aurelia-event-aggregator';
import * as $ from 'jquery';

@inject(EventAggregator)
export class GridStateService {
private _grid: any;
private _gridOptions: GridOption;
private _preset: GridState;
private filterService: FilterService;
private _filterSubcription: Subscription;
private _sorterSubcription: Subscription;
private sortService: SortService;

constructor(private ea: EventAggregator) { }

/**
* Initialize the Export Service
* @param grid
Expand All @@ -26,6 +34,19 @@ export class GridStateService {
this.filterService = filterService;
this.sortService = sortService;
this._gridOptions = (grid && grid.getOptions) ? grid.getOptions() : {};

// Subscribe to Event Emitter of Filter & Sort changed, go back to page 1 when that happen
this._filterSubcription = this.ea.subscribe('filterService:filterChanged', (currentFilters: CurrentFilter[]) => {
this.ea.publish('gridStateService:changed', { change: { newValues: currentFilters, type: GridStateType.filter }, gridState: this.getCurrentGridState() });
});
this._sorterSubcription = this.ea.subscribe('sortService:sortChanged', (currentSorters: CurrentSorter[]) => {
this.ea.publish('gridStateService:changed', { change: { newValues: currentSorters, type: GridStateType.sorter }, gridState: this.getCurrentGridState() });
});
}

dispose() {
this._filterSubcription.dispose();
this._sorterSubcription.dispose();
}

/**
Expand Down
Loading