Skip to content

Commit b887c03

Browse files
authored
Merge pull request #2859 from IgniteUI/add--custom-sort-strategy
feat(grid): expose input to set custom sort strategy per column #2734
2 parents 43eeacd + 8c77152 commit b887c03

13 files changed

+97
-28
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ For more detailed information, see the [README](https://github.com/IgniteUI/igni
4949
- A new boolean `hideGroupedColumns` input controls whether the grouped columns should be hidden as well (defaults to false).
5050
- **Breaking change** `cellClasses` input on `IgxColumnComponent` now accepts an object literal to allow conditional cell styling.
5151
- Exposing a mechanism for cells to grow according to their content.
52+
- `sortStrategy` input exposed to provide custom sort strategy for the `IgxColumnComponent`. The custom strategy should implement the `ISortingStrategy` interface, or can extend the base `SortingStrategy` class and override all or some of its public/protected members.
5253
- `igxFor`
5354
- Added support for variable heights.
5455
- `igx-datePicker` selector is deprecated. Use `igx-date-picker` selector instead.
@@ -155,6 +156,8 @@ When you focus a specific cell and press one of the following key combinations,
155156

156157
## 6.1.8
157158

159+
### General
160+
158161
### Bug fixes
159162

160163
- Fix sorting and groupby expression not syncing when there are already sorted columns. #2786

projects/igniteui-angular/src/lib/data-operations/sorting-expression.interface.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ISortingStrategy } from './sorting-strategy';
2+
13
/**
24
* Represents sorting expressions.
35
*/
@@ -11,4 +13,5 @@ export interface ISortingExpression {
1113
fieldName: string;
1214
dir: SortingDirection;
1315
ignoreCase?: boolean;
16+
strategy?: ISortingStrategy;
1417
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,18 @@ export class SortingStrategy implements ISortingStrategy {
3838
}
3939
return a > b ? 1 : a < b ? -1 : 0;
4040
}
41-
protected compareObjects(obj1: object, obj2: object, key: string, reverse: number, ignoreCase: boolean) {
41+
protected compareObjects(obj1: object, obj2: object, key: string, reverse: number, ignoreCase: boolean, strategy: ISortingStrategy) {
4242
let a = obj1[key];
4343
let b = obj2[key];
4444
if (ignoreCase) {
4545
a = a && a.toLowerCase ? a.toLowerCase() : a;
4646
b = b && b.toLowerCase ? b.toLowerCase() : b;
4747
}
48-
return reverse * this.compareValues(a, b);
48+
if (strategy) {
49+
return reverse * strategy.compareValues(a, b);
50+
} else {
51+
return reverse * this.compareValues(a, b);
52+
}
4953
}
5054
protected arraySort<T>(data: T[], compareFn?): T[] {
5155
return data.sort(compareFn);
@@ -78,7 +82,7 @@ export class SortingStrategy implements ISortingStrategy {
7882
false;
7983
const reverse = (expression.dir === SortingDirection.Desc ? -1 : 1);
8084
const cmpFunc = (obj1, obj2) => {
81-
return this.compareObjects(obj1, obj2, key, reverse, ignoreCase);
85+
return this.compareObjects(obj1, obj2, key, reverse, ignoreCase, expression.strategy);
8286
};
8387
return this.arraySort(data, cmpFunc);
8488
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ Inputs available on the **IgxGridColumnComponent** to define columns:
278278
|`field`|string|Column field name|
279279
|`header`|string|Column header text|
280280
|`sortable`|boolean|Set column to be sorted or not|
281+
|`sortStrategy`| Provide custom sort strategy to be used when sorting|
281282
|`editable`|boolean|Set column values to be editable|
282283
|`filterable`|boolean|Set column values to be filterable|
283284
|`hasSummary`| boolean |Sets whether or not the specific column has summaries enabled.|

projects/igniteui-angular/src/lib/grids/api.service.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { IgxRowComponent } from './row.component';
1313
import { IFilteringOperation } from '../data-operations/filtering-condition';
1414
import { IFilteringExpressionsTree, FilteringExpressionsTree } from '../data-operations/filtering-expressions-tree';
1515
import { Transaction, TransactionType } from '../services/index';
16+
import { ISortingStrategy } from '../data-operations/sorting-strategy';
17+
import { SortingStateDefaults } from '../data-operations/sorting-state.interface';
1618
/**
1719
*@hidden
1820
*/
@@ -328,13 +330,13 @@ export class GridBaseAPIService <T extends IgxGridBaseComponent> {
328330
}
329331
}
330332

331-
public sort(id: string, fieldName: string, dir: SortingDirection, ignoreCase: boolean): void {
333+
public sort(id: string, fieldName: string, dir: SortingDirection, ignoreCase: boolean, strategy: ISortingStrategy): void {
332334
if (dir === SortingDirection.None) {
333335
this.remove_grouping_expression(id, fieldName);
334336
}
335337
const sortingState = cloneArray(this.get(id).sortingExpressions);
336-
337-
this.prepare_sorting_expression([sortingState], { fieldName, dir, ignoreCase });
338+
strategy = strategy ? strategy : this.getSortStrategyPerColumn(id, fieldName);
339+
this.prepare_sorting_expression([sortingState], { fieldName, dir, ignoreCase, strategy });
338340
this.get(id).sortingExpressions = sortingState;
339341
}
340342

@@ -345,6 +347,7 @@ export class GridBaseAPIService <T extends IgxGridBaseComponent> {
345347
if (each.dir === SortingDirection.None) {
346348
this.remove_grouping_expression(id, each.fieldName);
347349
}
350+
each.strategy = each.strategy ? each.strategy : this.getSortStrategyPerColumn(id, each.fieldName);
348351
this.prepare_sorting_expression([sortingState], each);
349352
}
350353

@@ -493,4 +496,9 @@ export class GridBaseAPIService <T extends IgxGridBaseComponent> {
493496

494497
protected remove_grouping_expression(id, fieldName) {
495498
}
499+
500+
protected getSortStrategyPerColumn(id: string, fieldName: string) {
501+
return this.get_column_by_name(this.get(id).id, fieldName) ?
502+
this.get_column_by_name(id, fieldName).sortStrategy : undefined;
503+
}
496504
}

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
IgxDateFilteringOperand,
3232
IgxStringFilteringOperand } from '../data-operations/filtering-condition';
3333
import { IgxGridBaseComponent } from './grid-base.component';
34+
import { SortingStrategy } from '../data-operations/sorting-strategy';
3435
/**
3536
* **Ignite UI for Angular Column** -
3637
* [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid.html#columns-configuration)
@@ -480,6 +481,33 @@ export class IgxColumnComponent implements AfterContentInit {
480481
public set filters(classRef: any) {
481482
this._filters = classRef;
482483
}
484+
/**
485+
* Gets the column `sortStrategy`.
486+
* ```typescript
487+
* let sortStrategy = this.column.sortStrategy'
488+
* ```
489+
* @memberof IgxColumnComponent
490+
*/
491+
@Input()
492+
public get sortStrategy(): any {
493+
return this._sortStrategy;
494+
}
495+
/**
496+
* Sets the column `sortStrategy`.
497+
* ```typescript
498+
* this.column.sortStrategy = new CustomSortingStrategy().
499+
*
500+
* class CustomSortingStrategy extends SortingStrategy {
501+
* ...
502+
* }
503+
* ```
504+
* @memberof IgxColumnComponent
505+
*/
506+
public set sortStrategy(classRef: any) {
507+
this._sortStrategy = classRef;
508+
}
509+
510+
483511
/**
484512
* Gets the default minimum `width` of the column.
485513
* ```typescript
@@ -726,6 +754,10 @@ export class IgxColumnComponent implements AfterContentInit {
726754
*@hidden
727755
*/
728756
protected _filters = null;
757+
/**
758+
*@hidden
759+
*/
760+
protected _sortStrategy = new SortingStrategy();
729761
/**
730762
*@hidden
731763
*/

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3494,7 +3494,7 @@ export abstract class IgxGridBaseComponent implements OnInit, OnDestroy, AfterCo
34943494
* @hidden
34953495
*/
34963496
protected _sort(expression: ISortingExpression) {
3497-
this.gridAPI.sort(this.id, expression.fieldName, expression.dir, expression.ignoreCase);
3497+
this.gridAPI.sort(this.id, expression.fieldName, expression.dir, expression.ignoreCase, expression.strategy);
34983498
}
34993499

35003500
/**

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,13 @@ export class IgxGridHeaderComponent implements OnInit, DoCheck, AfterViewInit, O
191191
this.sortDirection + 1 > SortingDirection.Desc ? SortingDirection.Asc : SortingDirection.Desc
192192
: this.sortDirection + 1 > SortingDirection.Desc ? SortingDirection.None : this.sortDirection + 1;
193193
this.sortDirection = sortDir;
194-
this.grid.sort({ fieldName: this.column.field, dir: this.sortDirection, ignoreCase: this.column.sortingIgnoreCase });
194+
this.grid.sort({ fieldName: this.column.field, dir: this.sortDirection, ignoreCase: this.column.sortingIgnoreCase,
195+
strategy: this.column.sortStrategy });
195196
this.grid.onSortingDone.emit({
196197
dir: this.sortDirection,
197198
fieldName: this.column.field,
198-
ignoreCase: this.column.sortingIgnoreCase
199+
ignoreCase: this.column.sortingIgnoreCase,
200+
strategy: this.column.sortStrategy
199201
});
200202
}
201203
}

projects/igniteui-angular/src/lib/grids/grid/grid-api.service.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import { IGroupByExpandState } from '../../data-operations/groupby-expand-state.
55
import { DataUtil } from '../../data-operations/data-util';
66
import { cloneArray } from '../../core/utils';
77
import { ISortingExpression, SortingDirection } from '../../data-operations/sorting-expression.interface';
8+
import { ISortingStrategy } from '../../data-operations/sorting-strategy';
89

910
export class IgxGridAPIService extends GridBaseAPIService<IgxGridComponent> {
1011

11-
public groupBy(id: string, fieldName: string, dir: SortingDirection, ignoreCase: boolean): void {
12+
public groupBy(id: string, fieldName: string, dir: SortingDirection, ignoreCase: boolean, strategy: ISortingStrategy): void {
1213
const groupingState = cloneArray(this.get(id).groupingExpressions);
1314
const sortingState = cloneArray(this.get(id).sortingExpressions);
14-
15-
this.prepare_sorting_expression([sortingState, groupingState], { fieldName, dir, ignoreCase });
15+
strategy = strategy ? strategy : this.getSortStrategyPerColumn(id, fieldName);
16+
this.prepare_sorting_expression([sortingState, groupingState], { fieldName, dir, ignoreCase, strategy });
1617
this.get(id).groupingExpressions = groupingState;
1718
this.arrange_sorting_expressions(id);
1819
}
@@ -22,6 +23,7 @@ export class IgxGridAPIService extends GridBaseAPIService<IgxGridComponent> {
2223
const sortingState = cloneArray(this.get(id).sortingExpressions);
2324

2425
for (const each of expressions) {
26+
each.strategy = each.strategy ? each.strategy : this.getSortStrategyPerColumn(id, each.fieldName);
2527
this.prepare_sorting_expression([sortingState, groupingState], each);
2628
}
2729

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ export class IgxGridComponent extends IgxGridBaseComponent implements OnInit, Do
549549
* @hidden
550550
*/
551551
protected _groupBy(expression: ISortingExpression) {
552-
this._gridAPI.groupBy(this.id, expression.fieldName, expression.dir, expression.ignoreCase);
552+
this._gridAPI.groupBy(this.id, expression.fieldName, expression.dir, expression.ignoreCase, expression.strategy);
553553
}
554554

555555
/**

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ export class IgxGroupAreaDropDirective extends IgxDropDirective {
5959
const grid = <IgxGridComponent>column.grid;
6060
const isGrouped = grid.groupingExpressions.findIndex((item) => item.fieldName === column.field) !== -1;
6161
if (column.groupable && !isGrouped) {
62-
grid.groupBy({ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase });
62+
grid.groupBy({ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase,
63+
strategy: column.sortStrategy });
6364
}
6465
}
6566
}

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

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { HelperUtils } from '../../test-utils/helper-utils.spec';
1717

1818
import { configureTestSuite } from '../../test-utils/configure-suite';
1919
import { DataParent } from '../../test-utils/sample-test-data.spec';
20+
import { SortingStrategy } from '../../data-operations/sorting-strategy';
21+
import { SortingStateDefaults } from '../../data-operations/sorting-state.interface';
2022

2123
describe('IgxGrid - GroupBy', () => {
2224
configureTestSuite();
@@ -2635,45 +2637,48 @@ describe('IgxGrid - GroupBy', () => {
26352637
it('should update grouping expression when sorting a column first then grouping by it and changing sorting for it again', () => {
26362638
const fix = TestBed.createComponent(DefaultGridComponent);
26372639
const grid = fix.componentInstance.instance;
2640+
let strategy = new CustomSortingStrategy();
26382641
fix.componentInstance.enableSorting = true;
26392642
fix.detectChanges();
26402643

2641-
grid.sort({ fieldName: 'ID', dir: SortingDirection.Asc, ignoreCase: false });
2642-
2643-
expect(grid.sortingExpressions).toEqual([{ fieldName: 'ID', dir: SortingDirection.Asc, ignoreCase: false }]);
2644+
grid.sort({ fieldName: 'ID', dir: SortingDirection.Asc, ignoreCase: false, strategy: new CustomSortingStrategy() });
2645+
expect(grid.sortingExpressions).toEqual([{ fieldName: 'ID', dir: SortingDirection.Asc, ignoreCase: false, strategy: strategy }]);
26442646
expect(grid.groupingExpressions).toEqual([]);
26452647

2648+
strategy = SortingStateDefaults.strategy;
26462649
grid.groupBy({ fieldName: 'ID', dir: SortingDirection.Asc, ignoreCase: false });
26472650
grid.sort({ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false });
26482651

2649-
expect(grid.sortingExpressions).toEqual([{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false }]);
2650-
expect(grid.groupingExpressions).toEqual([{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false }]);
2652+
expect(grid.sortingExpressions).toEqual([{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false, strategy: strategy }]);
2653+
expect(grid.groupingExpressions).toEqual([{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false, strategy: strategy }]);
26512654
});
26522655

26532656
it('should update grouping expression when sorting a column first then grouping by another and changing sorting for it', () => {
26542657
const fix = TestBed.createComponent(DefaultGridComponent);
26552658
const grid = fix.componentInstance.instance;
2659+
const strategy = SortingStateDefaults.strategy;
26562660
fix.componentInstance.enableSorting = true;
26572661
fix.detectChanges();
26582662

26592663
grid.sort({ fieldName: 'Downloads', dir: SortingDirection.Asc, ignoreCase: false });
26602664
grid.sort({ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false });
26612665

26622666
expect(grid.sortingExpressions).toEqual([
2663-
{ fieldName: 'Downloads', dir: SortingDirection.Asc, ignoreCase: false },
2664-
{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false }
2667+
{ fieldName: 'Downloads', dir: SortingDirection.Asc, ignoreCase: false, strategy: strategy },
2668+
{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false, strategy: strategy }
26652669
]);
26662670
expect(grid.groupingExpressions).toEqual([]);
26672671

26682672
grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Asc, ignoreCase: false });
26692673
grid.sort({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false });
26702674

26712675
expect(grid.sortingExpressions).toEqual([
2672-
{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false },
2673-
{ fieldName: 'Downloads', dir: SortingDirection.Asc, ignoreCase: false },
2674-
{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false }
2676+
{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, strategy: strategy },
2677+
{ fieldName: 'Downloads', dir: SortingDirection.Asc, ignoreCase: false, strategy: strategy },
2678+
{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false, strategy: strategy }
26752679
]);
2676-
expect(grid.groupingExpressions).toEqual([{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false }]);
2680+
expect(grid.groupingExpressions).toEqual([{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false,
2681+
strategy: strategy }]);
26772682
});
26782683

26792684
function sendInput(element, text, fix) {
@@ -2809,3 +2814,6 @@ export class GroupByDataMoreColumnsComponent extends DataParent {
28092814
@ViewChild(IgxGridComponent, { read: IgxGridComponent })
28102815
public instance: IgxGridComponent;
28112816
}
2817+
2818+
export class CustomSortingStrategy extends SortingStrategy {
2819+
}

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core';
22
import { cloneArray } from '../../core/utils';
33
import { DataUtil } from '../../data-operations/data-util';
44
import { IGroupByExpandState } from '../../data-operations/groupby-expand-state.interface';
5-
import { IGroupByResult } from '../../data-operations/sorting-strategy';
5+
import { IGroupByResult, ISortingStrategy } from '../../data-operations/sorting-strategy';
66
import { IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
77
import { ISortingExpression } from '../../data-operations/sorting-expression.interface';
88
import { IgxGridComponent } from './grid.component';
@@ -23,14 +23,19 @@ export class IgxGridSortingPipe implements PipeTransform {
2323

2424
public transform(collection: any[], expressions: ISortingExpression | ISortingExpression[],
2525
id: string, pipeTrigger: number): any[] {
26-
27-
const state = { expressions: [] };
26+
let strategy: ISortingStrategy;
27+
const state = { expressions: [], strategy };
2828
state.expressions = this.gridAPI.get(id).sortingExpressions;
2929

3030
if (!state.expressions.length) {
3131
return collection;
3232
}
3333

34+
// DataUtil.sort needs a sorting strategy to start with, so it makes sense to start with the strategy from the first expression
35+
// sorting-strategy.ts, sortDataRecursive method then takes care and use the corresponding strategy for each expression
36+
strategy = expressions[0].strategy;
37+
state.strategy = strategy;
38+
3439
return DataUtil.sort(cloneArray(collection), state);
3540
}
3641
}

0 commit comments

Comments
 (0)