Skip to content

Commit 75700d1

Browse files
committed
feat(grid): Add grouping, fix bugs #5460
1 parent 079fa63 commit 75700d1

File tree

3 files changed

+239
-62
lines changed

3 files changed

+239
-62
lines changed

projects/igniteui-angular/src/lib/grids/state.directive.ts

+113-37
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Directive, Optional, Self, Input, NgModule, Inject, AfterViewInit } from '@angular/core';
1+
import { Directive, Optional, Self, Input, NgModule, Inject } from '@angular/core';
22
import { ISortingExpression } from '../data-operations/sorting-expression.interface';
33
import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../data-operations/filtering-expressions-tree';
44
import { IFilteringExpression } from '../data-operations/filtering-expression.interface';
@@ -7,6 +7,9 @@ import { INTERFACE_TOKEN } from './grid/grid.component';
77
import { IgxColumnComponent } from './columns/column.component';
88
import { IGroupingExpression } from '../data-operations/grouping-expression.interface';
99
import { IPagingState } from '../data-operations/paging-state.interface';
10+
import { DataType } from '../data-operations/data-util';
11+
import { IgxBooleanFilteringOperand, IgxNumberFilteringOperand, IgxDateFilteringOperand,
12+
IgxStringFilteringOperand } from '../data-operations/filtering-condition';
1013

1114
export interface IGridState {
1215
columns: IColumnState[];
@@ -32,6 +35,8 @@ interface IColumnState {
3235
pinned: boolean;
3336
sortable: boolean;
3437
filterable: boolean;
38+
editable: boolean;
39+
groupable: boolean;
3540
movable: boolean;
3641
hidden: boolean;
3742
dataType: string;
@@ -56,7 +61,7 @@ const ACTION_SELECTION = 'selection';
5661
@Directive({
5762
selector: '[igxGridState]'
5863
})
59-
export class IgxGridStateDirective implements AfterViewInit {
64+
export class IgxGridStateDirective {
6065

6166
private _options: IGridStateOptions = {
6267
columns: true,
@@ -74,27 +79,37 @@ export class IgxGridStateDirective implements AfterViewInit {
7479
/**
7580
* An object with options determining if a certain feature state should be saved.
7681
*
82+
* ```html
83+
* <igx-grid [igxGridState]="options"></igx-grid>
84+
* ```
7785
* ```typescript
78-
* // get the row data for the first selected row
79-
* let saveSortingState = this.grid.state.options.sorting;
86+
* public options = {selection: false, advancedFiltering: false};
8087
* ```
8188
*/
8289
@Input('igxGridState')
8390
public get options(): IGridStateOptions {
8491
return this._options;
8592
}
8693

87-
public set options(val: IGridStateOptions) {
88-
Object.assign(this._options, val);
94+
public set options(value: IGridStateOptions) {
95+
Object.assign(this._options, value);
8996
}
9097

9198
constructor(@Inject(INTERFACE_TOKEN) @Self() @Optional() private grid) { }
9299

93-
public ngAfterViewInit() {
94-
this.restoreGridState(this.state);
95-
// this.grid.cdr.detectChanges();
96-
}
97-
100+
/**
101+
* Sets the state of a feature or states of all grid features, depending on the state object passed as an argument.
102+
* Pass an IGridState object to set the state for all features, or IPagingState object to set the paging state only.
103+
* returns an object containing all grid features states that are enabled through the `options` property.
104+
* ```html
105+
* <igx-grid [igxGridState]="options"></igx-grid>
106+
* ```
107+
* ```typescript
108+
* @ViewChild(IgxGridStateDirective, { static: true }) public state;
109+
* const gridState = window.localStorage.getItem(key);
110+
* this.state.setState(gridState);
111+
* ```
112+
*/
98113
public setState(state: IGridState |
99114
IColumnState |
100115
IFilteringExpressionsTree |
@@ -106,9 +121,23 @@ export class IgxGridStateDirective implements AfterViewInit {
106121
}
107122
this.state = state as IGridState | IColumnState | IFilteringExpressionsTree |
108123
ISortingExpression | IGroupingExpression | IPagingState;
124+
this.restoreGridState(this.state);
109125
}
110126

111-
public getState(feature?: string | string[], serialize = true): IGridState |
127+
/**
128+
* Gets the state of a feature or states of all grid features.
129+
* If a feature name is not passed as an argument,
130+
* returns an object containing all grid features states that are enabled through the `options` property.
131+
* The optional `serialize` argument determines whether the returned object will be serialized to a JSON string. Default value is false.
132+
* ```html
133+
* <igx-grid [igxGridState]="options"></igx-grid>
134+
* ```
135+
* ```typescript
136+
* @ViewChild(IgxGridStateDirective, { static: true }) public state;
137+
* let state = this.state.getState();
138+
* ```
139+
*/
140+
public getState(serialize = true, feature?: string | string[]): IGridState |
112141
IColumnState |
113142
IFilteringExpressionsTree |
114143
ISortingExpression |
@@ -123,25 +152,33 @@ export class IgxGridStateDirective implements AfterViewInit {
123152
if (feature) {
124153
if (Array.isArray(feature)) {
125154
feature.forEach(f => {
126-
state[f] = this.getGridFeature(f, serialize);
155+
state[f] = this.getGridFeature(f);
127156
});
128157
} else {
129-
state[feature] = this.getGridFeature(feature, serialize);
158+
state[feature] = this.getGridFeature(feature);
130159
}
131160
} else {
132-
state = this.getAllGridFeatures(serialize) as IGridState;
161+
state = this.getAllGridFeatures() as IGridState;
162+
}
163+
if (serialize) {
164+
state = JSON.stringify(state, this.stringifyCallback);
165+
return state as string;
166+
} else {
167+
return state as IGridState;
133168
}
134-
return state;
135169
}
136170

171+
/**
172+
* Helper method that creates a new array with the current grid columns.
173+
*/
137174
public restoreGridState(state) {
138175
for (const key of Object.keys(state)) {
139176
this.restoreFeature(key, state[key]);
140177
}
141178
}
142179

143180
/**
144-
* Applies the state for a given feature.
181+
* Restores the state of a feature.
145182
*/
146183
private restoreFeature(feature: string, state: any) {
147184
switch (feature) {
@@ -176,7 +213,10 @@ export class IgxGridStateDirective implements AfterViewInit {
176213
}
177214
}
178215

179-
private getAllGridFeatures(serialize = false): IGridState | string {
216+
/**
217+
* Returns an object containing all grid features state.
218+
*/
219+
private getAllGridFeatures(): IGridState {
180220
let gridState = {};
181221

182222
for (const key of Object.keys(this.options)) {
@@ -187,15 +227,14 @@ export class IgxGridStateDirective implements AfterViewInit {
187227
}
188228

189229
gridState = Object.assign({}, gridState);
190-
if (serialize) {
191-
gridState = JSON.stringify(gridState, this.stringifyCallback);
192-
return gridState as string;
193-
} else {
194-
return gridState as IGridState;
195-
}
230+
return gridState as IGridState;
196231
}
197232

198-
private getGridFeature(feature: string, serialize = false) {
233+
/**
234+
* Restores an object containing the state for a grid feature.
235+
* `serialize` param determines whether the returned object will be serialized to a JSON string. Default value is false.,
236+
*/
237+
private getGridFeature(feature: string) {
199238
let state = null;
200239
switch (feature) {
201240
case ACTION_COLUMNS: {
@@ -227,12 +266,7 @@ export class IgxGridStateDirective implements AfterViewInit {
227266
break;
228267
}
229268
}
230-
if (serialize) {
231-
state = JSON.stringify(state, this.stringifyCallback);
232-
return state as string;
233-
} else {
234-
return state;
235-
}
269+
return state;
236270
}
237271

238272
/**
@@ -244,6 +278,7 @@ export class IgxGridStateDirective implements AfterViewInit {
244278
pinned: c.pinned,
245279
sortable: c.sortable,
246280
filterable: c.filterable,
281+
editable: c.editable,
247282
movable: c.movable,
248283
hidden: c.hidden,
249284
dataType: c.dataType,
@@ -278,7 +313,7 @@ export class IgxGridStateDirective implements AfterViewInit {
278313
}
279314

280315
private getGroupBy() {
281-
const groupingState = this.grid.groupbyExpressions;
316+
const groupingState = this.grid.groupingExpressions;
282317
return { groupby: groupingState };
283318
}
284319

@@ -288,7 +323,7 @@ export class IgxGridStateDirective implements AfterViewInit {
288323
}
289324

290325
/**
291-
* This method modifies the grid column list to restore the columns.
326+
* Restores the grid columns by modifying the `columnList` collection of the grid.
292327
*/
293328
private restoreColumns(columns: IColumnState[]): void {
294329
const newColumns = [];
@@ -298,6 +333,8 @@ export class IgxGridStateDirective implements AfterViewInit {
298333
ref.instance.field = col.field;
299334
ref.instance.dataType = col.dataType;
300335
ref.instance.sortable = col.sortable;
336+
ref.instance.groupable = col.groupable;
337+
ref.instance.editable = col.editable;
301338
ref.instance.filterable = col.filterable;
302339
ref.instance.resizable = col.resizable;
303340
ref.instance.movable = col.movable;
@@ -314,15 +351,25 @@ export class IgxGridStateDirective implements AfterViewInit {
314351
this.grid.columnList.notifyOnChanges();
315352
}
316353

354+
/**
355+
* Restores the grid filtering state, i.e. sets the `filteringExpressionsTree` property value.
356+
*/
317357
private restoreFiltering(state: FilteringExpressionsTree) {
318358
const filterTree = this.createExpressionsTreeFromObject(state);
319359
this.grid.filteringExpressionsTree = filterTree;
320360
}
321361

362+
/**
363+
* Restores the grid advanced filtering state, i.e. sets the `advancedFilteringExpressionsTree` property value.
364+
*/
322365
private restoreAdvancedFiltering(state) {
323366
const advFilterTree = this.createExpressionsTreeFromObject(state.advancedFiltering);
324367
this.grid.advancedFilteringExpressionsTree = advFilterTree;
325368
}
369+
370+
/**
371+
* Restores the grid sorting state, i.e. sets the `sortingExpressions` property value.
372+
*/
326373
private restoreSorting(state: ISortingExpression | ISortingExpression[]) {
327374
const strategy = DefaultSortingStrategy.instance();
328375

@@ -335,6 +382,9 @@ export class IgxGridStateDirective implements AfterViewInit {
335382
this.grid.sortingExpressions = state;
336383
}
337384

385+
/**
386+
* Restores the grid grouping state, i.e. sets the `groupbyExpressions` property value.
387+
*/
338388
private restoreGroupBy(state: IGroupingExpression | IGroupingExpression[]) {
339389
const strategy = DefaultSortingStrategy.instance();
340390

@@ -344,9 +394,12 @@ export class IgxGridStateDirective implements AfterViewInit {
344394
(state as IGroupingExpression).strategy = strategy;
345395
}
346396

347-
this.grid.groupbyExpressions = state;
397+
this.grid.groupingExpressions = state;
348398
}
349399

400+
/**
401+
* Restores the grid paging state, i.e. sets the `perPage` property value and paginate to index.
402+
*/
350403
private restorePaging(state: IPagingState) {
351404
if (this.grid.perPage !== state.recordsPerPage) {
352405
this.grid.perPage = state.recordsPerPage;
@@ -378,16 +431,39 @@ export class IgxGridStateDirective implements AfterViewInit {
378431
expressionsTree.filteringOperands.push(subTree);
379432
} else {
380433
const expr = item as IFilteringExpression;
381-
const column = this.grid.getColumnByName(expr.fieldName);
382-
expr.condition = column.filters.condition(expr.condition.name);
383-
expr.searchVal = (column.dataType === 'date') ? new Date(Date.parse(expr.searchVal)) : expr.searchVal;
434+
const dataType = this.state[ACTION_COLUMNS].find(c => c.field === expr.fieldName).dataType;
435+
expr.condition = this.generateFilteringCondition(dataType, expr.condition.name);
436+
expr.searchVal = (dataType === 'date') ? new Date(Date.parse(expr.searchVal)) : expr.searchVal;
384437
expressionsTree.filteringOperands.push(expr);
385438
}
386439
}
387440

388441
return expressionsTree;
389442
}
390443

444+
/**
445+
* Returns the filtering logic function for a given dataType and condition (contains, greaterThan, etc.)
446+
*/
447+
private generateFilteringCondition(dataType: string, name: string): any {
448+
let filters;
449+
switch (dataType) {
450+
case DataType.Boolean:
451+
filters = IgxBooleanFilteringOperand.instance();
452+
break;
453+
case DataType.Number:
454+
filters = IgxNumberFilteringOperand.instance();
455+
break;
456+
case DataType.Date:
457+
filters = IgxDateFilteringOperand.instance();
458+
break;
459+
case DataType.String:
460+
default:
461+
filters = IgxStringFilteringOperand.instance();
462+
break;
463+
}
464+
return filters.condition(name);
465+
}
466+
391467
private stringifyCallback(key: string, val: any) {
392468
if (key === 'searchVal' && val instanceof Set) {
393469
return Array.from(val);

src/app/grid-state/grid-state.component.html

+30-6
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,39 @@
2424
<igx-switch [(ngModel)]="options.sorting" (change)="onChange($event, 'sorting')">Sorting</igx-switch>
2525
</div>
2626
<div class="control-item">
27-
<igx-switch [(ngModel)]="options.groupby" (change)="onChange($event, 'sorting')">Sorting</igx-switch>
27+
<igx-switch [(ngModel)]="options.groupby" (change)="onChange($event, 'groupby')">Grouping</igx-switch>
2828
</div>
2929
<div class="control-item">
3030
<igx-switch [(ngModel)]="options.columns" (change)="onChange($event, 'columns')">Columns</igx-switch>
3131
</div>
3232
<div class="control-item">
33-
<button igxButton="raised" (click)="restoreGridState()">Restore</button>
33+
<igx-switch [(ngModel)]="serialize" (change)="onSerializeChange($event)">Serialize</igx-switch>
34+
</div>
35+
</div>
36+
<div class="switches" style="min-width: inherit">
37+
<div class="control-item">
38+
<button igxButton="raised" (click)="restoreGridState()">Restore All</button>
39+
</div>
40+
<div class="control-item">
41+
<button igxButton="raised" (click)="restoreColumns()">Columns</button>
42+
</div>
43+
<div class="control-item">
44+
<button igxButton="raised" (click)="restoreFiltering()">Filtering</button>
45+
</div>
46+
<div class="control-item">
47+
<button igxButton="raised" (click)="restoreSorting()">Sorting</button>
48+
</div>
49+
<div class="control-item">
50+
<button igxButton="raised" (click)="restoreGroupby()">GroupBy</button>
51+
</div>
52+
<div class="control-item">
53+
<button igxButton="raised" (click)="restoreSelection">Selection</button>
54+
</div>
55+
<div class="control-item">
56+
<button igxButton="raised" (click)="restorePaging()">Paging</button>
57+
</div>
58+
<div class="control-item">
59+
<button igxButton="raised" (click)="resetGridState()">Reset</button>
3460
</div>
3561
<div class="control-item">
3662
<span class="clear-button" (click)="clearStorage(toast)" #target="tooltipTarget" [igxTooltipTarget]="clear"><igx-icon>delete</igx-icon></span>
@@ -42,16 +68,14 @@
4268
</div>
4369

4470
<igx-grid [id]="gridId" #grid1 [igxGridState]="options" [data]="localData" [primaryKey]="'ProductID'" width="1200px" height="550px"
45-
[rowEditable]="true" [allowFiltering]="true" [allowAdvancedFiltering]="true" [rowSelectable]="true"
71+
[rowEditable]="false" [allowFiltering]="true" [allowAdvancedFiltering]="true"
4672
[paging]="true" [showToolbar]="true" [columnPinning]="true" [columnHiding]="true"
4773
[filterMode]="'excelStyleFilter'" [displayDensity]="'cosy'">
48-
<igx-column *ngFor="let c of columns" [sortable]="true" [movable]="true" [filterable]="true"
74+
<igx-column *ngFor="let c of columns" [sortable]="c.sortable" [movable]="c.movable" [editable]="true" [filterable]="c.filterable" [groupable]="c.groupable"
4975
[field]="c.field" [header]="c.header" [dataType]="c.dataType" [pinned]="c.pinned" [hidden]="c.hidden">
5076
</igx-column>
5177
</igx-grid>
5278

5379
<div #clear="tooltip" igxTooltip>Clear the state from localStorage.</div>
5480
<div #reload="tooltip" igxTooltip>Reload the page.</div>
55-
56-
<igx-toast #toast message="Grid state data has been cleared from localStorage."></igx-toast>
5781
</div>

0 commit comments

Comments
 (0)