Skip to content

Commit daa8d75

Browse files
authored
Merge pull request #10339 from IgniteUI/sstoychev/groupingstrategy-master
feat(groupby): adding grouping strategy option #10223 - master
2 parents ab76218 + 8b14030 commit daa8d75

File tree

10 files changed

+117
-37
lines changed

10 files changed

+117
-37
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ All notable changes for each version of this project will be documented in this
4747
- Inputs `showToolbar`, `toolbarTitle`, `columnHiding`, `columnHidingTitle`, `hiddenColumnsText`,
4848
`columnPinning`, `columnPinningTitle`, `pinnedColumnsText`.
4949
Use `IgxGridToolbarComponent`, `IgxGridToolbarHidingComponent`, `IgxGridToolbarPinningComponent` instead.
50+
- `igxGrid`
51+
- Exposed a `groupStrategy` input that functions similarly to `sortStrategy`, allowing customization of the grouping behavior of the grid. Please, refer to the [Group By ](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/groupby) topic for more information.
5052
- `IgxColumnActionsComponent`
5153
- **Breaking Change** - The following input has been removed
52-
- Input `columns`. Use `igxGrid` `columns` input instead.
54+
- Input `columns`. Use `igxGrid` `columns` input instead.
5355
- `IgxCarousel`
5456
- **Breaking Changes** -The carousel animation type `CarouselAnimationType` is renamed to `HorizontalAnimationType`.
5557

projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ const testGroupBy = () => {
263263
// sort
264264
const res = DataUtil.sort(data, [expr]);
265265
// group by
266-
DataUtil.group(res, state, null, groupRecords);
266+
DataUtil.group(res, state, undefined, null, groupRecords);
267267
expect(groupRecords.length).toEqual(2);
268268
expect(groupRecords[0].records.length).toEqual(3);
269269
expect(groupRecords[1].records.length).toEqual(2);
@@ -279,7 +279,7 @@ const testGroupBy = () => {
279279
// sort
280280
const sorted = DataUtil.sort(data, [expr, expr2]);
281281
// group by
282-
DataUtil.group(sorted, state, null, groupRecords);
282+
DataUtil.group(sorted, state, undefined, null, groupRecords);
283283
expect(groupRecords.length).toEqual(2);
284284
expect(groupRecords[0].records.length).toEqual(3);
285285
expect(groupRecords[1].records.length).toEqual(2);

projects/igniteui-angular/src/lib/data-operations/data-util.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { IGroupByRecord } from './groupby-record.interface';
1111
import { IGroupingState } from './groupby-state.interface';
1212
import { ISortingExpression } from './sorting-expression.interface';
1313
import { FilteringStrategy } from './filtering-strategy';
14+
import { IGridGroupingStrategy } from './grouping-strategy';
1415
import { ITreeGridRecord } from '../grids/tree-grid/public_api';
1516
import { cloneValue, mergeObjects, mkenum } from '../core/utils';
1617
import { Transaction, TransactionType, HierarchicalTransaction } from '../services/transaction/transaction';
@@ -73,9 +74,8 @@ export class DataUtil {
7374
return rec;
7475
}
7576

76-
public static group<T>(data: T[], state: IGroupingState, grid: GridType = null,
77+
public static group<T>(data: T[], state: IGroupingState, grouping: IGridGroupingStrategy = new IgxGrouping(), grid: GridType = null,
7778
groupsRecords: any[] = [], fullResult: IGroupByResult = { data: [], metadata: [] }): IGroupByResult {
78-
const grouping = new IgxGrouping();
7979
groupsRecords.splice(0, groupsRecords.length);
8080
return grouping.groupBy(data, state, grid, groupsRecords, fullResult);
8181
}

projects/igniteui-angular/src/lib/data-operations/grouping-strategy.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import { IGroupByRecord } from './groupby-record.interface';
2-
import { IgxSorting } from './sorting-strategy';
2+
import { IgxSorting, IGridSortingStrategy } from './sorting-strategy';
33
import { IGroupingState } from './groupby-state.interface';
44
import { IGroupByResult } from './grouping-result.interface';
55

6-
export class IgxGrouping extends IgxSorting {
6+
7+
export interface IGridGroupingStrategy extends IGridSortingStrategy {
8+
groupBy(data: any[], state: IGroupingState, grid?: any, groupsRecords?: any[], fullResult?: IGroupByResult): IGroupByResult;
9+
}
10+
11+
export class IgxGrouping extends IgxSorting implements IGridGroupingStrategy {
712
public groupBy(data: any[], state: IGroupingState, grid?: any,
813
groupsRecords?: any[], fullResult: IGroupByResult = { data: [], metadata: [] }): IGroupByResult {
914
const metadata: IGroupByRecord[] = [];

projects/igniteui-angular/src/lib/grids/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ Below is the list of all inputs that the developers may set to configure the gri
185185
|`evenRowCSS`|string|Additional styling classes applied to all even rows in the grid.|
186186
|`oddRowCSS`|string|Additional styling classes applied to all odd rows in the grid.|
187187
|`paginationTemplate`|TemplateRef|You can provide a custom `ng-template` for the pagination part of the grid.|
188+
|`groupStrategy`| IGridGroupingStrategy | Provides custom group strategy to be used when grouping |
188189
|`groupingExpressions`| Array | The group by state of the grid.
189190
|`groupingExpansionState`| Array | The list of expansion states of the group rows. Contains the expansion state(expanded: boolean) and an unique identifier for the group row (Array<IGroupByExpandState>) that contains a list of the group row's parents described via their fieldName and value.
190191
|`groupsExpanded`| boolean | Determines whether created groups are rendered expanded or collapsed. |

projects/igniteui-angular/src/lib/grids/grid/grid.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
| visibleColumns:hasVisibleColumns
7272
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger
7373
| gridSort:sortingExpressions:sortStrategy:id:pipeTrigger
74-
| gridGroupBy:groupingExpressions:groupingExpansionState:groupsExpanded:id:groupsRecords:pipeTrigger
74+
| gridGroupBy:groupingExpressions:groupingExpansionState:groupStrategy:groupsExpanded:id:groupsRecords:pipeTrigger
7575
| gridPaging:paginator?.page:paginator?.perPage:id:pipeTrigger
7676
| gridSummary:hasSummarizedColumns:summaryCalculationMode:summaryPosition:id:showSummaryOnCollapse:pipeTrigger:summaryPipeTrigger
7777
| gridDetails:hasDetails:expansionStates:pipeTrigger

projects/igniteui-angular/src/lib/grids/grid/grid.component.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { IgxGridGroupByAreaComponent } from '../grouping/grid-group-by-area.comp
3333
import { IgxGridCell } from '../grid-public-cell';
3434
import { CellType } from '../common/cell.interface';
3535
import { DeprecateMethod } from '../../core/deprecateDecorators';
36+
import { IGridGroupingStrategy } from '../../data-operations/grouping-strategy';
3637

3738
let NEXT_ID = 0;
3839

@@ -263,6 +264,10 @@ export class IgxGridComponent extends IgxGridBaseDirective implements GridType,
263264
* @hidden
264265
*/
265266
protected _groupAreaTemplate: TemplateRef<any>;
267+
/**
268+
* @hidden
269+
*/
270+
protected _groupStrategy: IGridGroupingStrategy;
266271
/**
267272
* @hidden
268273
*/
@@ -455,6 +460,25 @@ export class IgxGridComponent extends IgxGridBaseDirective implements GridType,
455460
this._hideGroupedColumns = value;
456461
}
457462

463+
/**
464+
* Gets/Sets the grouping strategy of the grid.
465+
*
466+
* @remarks The default IgxGrouping extends from IgxSorting and a custom one can be used as a `sortStrategy` as well.
467+
*
468+
* @example
469+
* ```html
470+
* <igx-grid #grid [data]="localData" [groupStrategy]="groupStrategy"></igx-grid>
471+
* ```
472+
*/
473+
@Input()
474+
public get groupStrategy(): IGridGroupingStrategy {
475+
return this._groupStrategy;
476+
}
477+
478+
public set groupStrategy(value: IGridGroupingStrategy) {
479+
this._groupStrategy = value;
480+
}
481+
458482
/**
459483
* Gets/Sets the message displayed inside the GroupBy drop area where columns can be dragged on.
460484
*

projects/igniteui-angular/src/lib/grids/grid/grid.groupby.spec.ts

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { GridSelectionFunctions, GridFunctions } from '../../test-utils/grid-fun
2020
import { GridSelectionMode } from '../common/enums';
2121
import { ControlsFunction } from '../../test-utils/controls-functions.spec';
2222
import { IGroupingExpression } from '../../data-operations/grouping-expression.interface';
23+
import { IgxGrouping } from '../../data-operations/grouping-strategy';
2324

2425
describe('IgxGrid - GroupBy #grid', () => {
2526

@@ -602,6 +603,21 @@ describe('IgxGrid - GroupBy #grid', () => {
602603
expect(grid.groupingExpressionsChange.emit).toHaveBeenCalledTimes(0);
603604
}));
604605

606+
it('should group unbound column with custom grouping strategy', fakeAsync(() => {
607+
const fix = TestBed.createComponent(GroupableGridComponent);
608+
fix.componentInstance.data.forEach((r, i) => {
609+
r['fieldValue1'] = Math.floor(i / 3);
610+
r['fieldValue2'] = Math.floor(i / 4);
611+
});
612+
fix.detectChanges();
613+
fix.componentInstance.instance.groupBy({
614+
fieldName: 'UnboundField', dir: SortingDirection.Desc, ignoreCase: false
615+
});
616+
fix.detectChanges();
617+
const groupRows = fix.componentInstance.instance.groupsRowList.toArray();
618+
expect(groupRows.length).toEqual(4);
619+
}));
620+
605621
// GroupBy + Sorting integration
606622
it('should apply sorting on each group\'s records when non-grouped column is sorted.', fakeAsync(() => {
607623
const fix = TestBed.createComponent(DefaultGridComponent);
@@ -1848,36 +1864,36 @@ describe('IgxGrid - GroupBy #grid', () => {
18481864
}));
18491865

18501866
it('Should have the correct properties in the custom row selector template', fakeAsync(() => {
1851-
const fix = TestBed.createComponent(GridGroupByRowCustomSelectorsComponent);
1852-
const grid = fix.componentInstance.instance;
1853-
fix.componentInstance.width = '1200px';
1854-
tick();
1855-
grid.columnWidth = '200px';
1856-
tick();
1857-
grid.rowSelection = GridSelectionMode.multiple;
1858-
tick();
1859-
fix.detectChanges();
1867+
const fix = TestBed.createComponent(GridGroupByRowCustomSelectorsComponent);
1868+
const grid = fix.componentInstance.instance;
1869+
fix.componentInstance.width = '1200px';
1870+
tick();
1871+
grid.columnWidth = '200px';
1872+
tick();
1873+
grid.rowSelection = GridSelectionMode.multiple;
1874+
tick();
1875+
fix.detectChanges();
18601876

1861-
grid.groupBy({
1862-
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
1863-
});
1864-
tick();
1865-
fix.detectChanges();
1877+
grid.groupBy({
1878+
fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false
1879+
});
1880+
tick();
1881+
fix.detectChanges();
18661882

1867-
const grRow = grid.groupsRowList.toArray()[0];
1868-
const contextSelect = { selectedCount: 0, totalCount: 2, groupRow: grid.groupsRowList.toArray()[0].groupRow };
1869-
const contextUnselect = { selectedCount: 2, totalCount: 2, groupRow: grid.groupsRowList.toArray()[0].groupRow };
1883+
const grRow = grid.groupsRowList.toArray()[0];
1884+
const contextSelect = { selectedCount: 0, totalCount: 2, groupRow: grid.groupsRowList.toArray()[0].groupRow };
1885+
const contextUnselect = { selectedCount: 2, totalCount: 2, groupRow: grid.groupsRowList.toArray()[0].groupRow };
18701886

1871-
spyOn(fix.componentInstance, 'onGroupByRowClick').and.callThrough();
1887+
spyOn(fix.componentInstance, 'onGroupByRowClick').and.callThrough();
18721888

1873-
grRow.nativeElement.querySelector('.igx-checkbox__composite').click();
1874-
fix.detectChanges();
1875-
expect(fix.componentInstance.onGroupByRowClick).toHaveBeenCalledWith(fix.componentInstance.groupByRowClick, contextSelect);
1889+
grRow.nativeElement.querySelector('.igx-checkbox__composite').click();
1890+
fix.detectChanges();
1891+
expect(fix.componentInstance.onGroupByRowClick).toHaveBeenCalledWith(fix.componentInstance.groupByRowClick, contextSelect);
18761892

1877-
grRow.nativeElement.querySelector('.igx-checkbox__composite').click();
1878-
fix.detectChanges();
1879-
expect(fix.componentInstance.onGroupByRowClick).toHaveBeenCalledWith(fix.componentInstance.groupByRowClick, contextUnselect);
1880-
}));
1893+
grRow.nativeElement.querySelector('.igx-checkbox__composite').click();
1894+
fix.detectChanges();
1895+
expect(fix.componentInstance.onGroupByRowClick).toHaveBeenCalledWith(fix.componentInstance.groupByRowClick, contextUnselect);
1896+
}));
18811897

18821898
// GroupBy + Resizing
18831899
it('should retain same size for group row after a column is resized.', fakeAsync(() => {
@@ -3488,19 +3504,42 @@ export class DefaultGridComponent extends DataParent {
34883504
}
34893505
}
34903506

3507+
const formatUnboundValueFunction = (rowData: any | undefined): string | undefined => rowData.fieldValue1 + ' ' + rowData.fieldValue2;
3508+
3509+
3510+
class MySortingStrategy extends IgxGrouping {
3511+
protected getFieldValue(
3512+
obj: any,
3513+
key: string,
3514+
isDate = false,
3515+
isTime = false
3516+
): unknown {
3517+
if (key !== 'UnboundField') {
3518+
return super.getFieldValue(obj, key, isDate, isTime);
3519+
}
3520+
3521+
return formatUnboundValueFunction(obj);
3522+
}
3523+
}
3524+
34913525
@Component({
34923526
template: `
34933527
<igx-grid
34943528
[width]='width'
34953529
[height]='height'
3496-
[data]="data">
3530+
[data]="data"
3531+
[sortStrategy]="sortStrategy"
3532+
[groupStrategy]="groupStrategy">
34973533
<igx-column [field]="'ID'" [header]="'ID'" [width]="200" [groupable]="true" [hasSummary]="false"></igx-column>
34983534
<igx-column [field]="'ReleaseDate'" [header]="'ReleaseDate'" [width]="200" [groupable]="true" [hasSummary]="false"
34993535
dataType="date"></igx-column>
35003536
<igx-column [field]="'Downloads'" [header]="'Downloads'" [width]="200" [groupable]="true" [hasSummary]="false"
35013537
dataType="number"></igx-column>
35023538
<igx-column [field]="'ProductName'" [header]="'ProductName'" [width]="200" [groupable]="true" [hasSummary]="false"></igx-column>
35033539
<igx-column [field]="'Released'" [header]="'Released'" [width]="200" [groupable]="true" [hasSummary]="false"></igx-column>
3540+
<igx-column [field]="'UnboundField'" [header]="'Unbound Value'" [width]="200" [dataType]="'string'"
3541+
[sortable]="true" [groupable]="true" [formatter]="formatUnboundValue">
3542+
</igx-column>
35043543
<igx-paginator></igx-paginator>
35053544
</igx-grid>
35063545
`
@@ -3511,6 +3550,13 @@ export class GroupableGridComponent extends DataParent {
35113550

35123551
public width = '800px';
35133552
public height = '700px';
3553+
3554+
public sortStrategy = new MySortingStrategy();
3555+
public groupStrategy = this.sortStrategy;
3556+
3557+
public formatUnboundValue(value: string, rowData: any | undefined): string | undefined {
3558+
return formatUnboundValueFunction(rowData);
3559+
}
35143560
}
35153561

35163562
@Component({

projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { IgxGridBaseDirective } from '../grid-base.directive';
1313
import { GridType } from '../common/grid.interface';
1414
import { IFilteringStrategy } from '../../data-operations/filtering-strategy';
1515
import { IGridSortingStrategy } from '../../data-operations/sorting-strategy';
16+
import { IGridGroupingStrategy } from '../../data-operations/grouping-strategy';
1617
import { GridPagingMode } from '../common/enums';
1718

1819
/**
@@ -60,7 +61,8 @@ export class IgxGridGroupingPipe implements PipeTransform {
6061
}
6162

6263
public transform(collection: any[], expression: IGroupingExpression | IGroupingExpression[],
63-
expansion: IGroupByExpandState | IGroupByExpandState[], defaultExpanded: boolean,
64+
expansion: IGroupByExpandState | IGroupByExpandState[],
65+
groupingStrategy: IGridGroupingStrategy, defaultExpanded: boolean,
6466
id: string, groupsRecords: any[], _pipeTrigger: number): IGroupByResult {
6567

6668
const state = { expressions: [], expansion: [], defaultExpanded };
@@ -79,7 +81,7 @@ export class IgxGridGroupingPipe implements PipeTransform {
7981
} else {
8082
state.expansion = grid.groupingExpansionState;
8183
state.defaultExpanded = grid.groupsExpanded;
82-
result = DataUtil.group(cloneArray(collection), state, grid, groupsRecords, fullResult);
84+
result = DataUtil.group(cloneArray(collection), state, groupingStrategy, grid, groupsRecords, fullResult);
8385
}
8486
grid.groupingFlatResult = result.data;
8587
grid.groupingResult = fullResult.data;

projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ export abstract class IgxBaseExporter {
694694

695695
if (hasGrouping && !this.options.ignoreGrouping) {
696696
const groupsRecords = [];
697-
DataUtil.group(cloneArray(gridData), groupedGridGroupingState, grid, groupsRecords);
697+
DataUtil.group(cloneArray(gridData), groupedGridGroupingState, grid.groupStrategy, grid, groupsRecords);
698698
gridData = groupsRecords;
699699
}
700700

0 commit comments

Comments
 (0)