Skip to content

Commit baae865

Browse files
authored
Merge pull request #10150 from IgniteUI/mkirova/pivot-grid-levels
Add handling for levels and expand/collapse indicators.
2 parents 8c5e15b + a0d0690 commit baae865

File tree

10 files changed

+193
-26
lines changed

10 files changed

+193
-26
lines changed

projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-component.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@
184184
@extend %igx-grid__tr--inner !optional;
185185
}
186186

187+
@include e(tr, $m: header) {
188+
@extend %igx-grid__tr--header !optional;
189+
}
190+
187191
@include e(tr, $m: group) {
188192
@extend %grid-row--group !optional;
189193
}

projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,11 @@
12171217
background: inherit;
12181218
}
12191219

1220+
%igx-grid__tr--header {
1221+
display: flex;
1222+
align-items: center;
1223+
}
1224+
12201225
%igx-grid__tr--add-animate {
12211226
@include animation(scale-in-ver-center .2s $ease-in-out-quad);
12221227
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2859,6 +2859,9 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
28592859
public summaryPipeTrigger = 0;
28602860

28612861
public EMPTY_DATA = [];
2862+
2863+
public isPivot = false;
2864+
28622865
/**
28632866
* @hidden
28642867
*/
@@ -2950,6 +2953,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
29502953
protected _userOutletDirective: IgxOverlayOutletDirective;
29512954
protected _transactions: TransactionService<Transaction, State>;
29522955
protected _batchEditing = false;
2956+
protected _autoGeneratedCols = [];
29532957

29542958
/** @hidden @internal */
29552959
public get paginator() {
@@ -3022,7 +3026,6 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
30223026
private _unpinnedWidth = NaN;
30233027
private _visibleColumns = [];
30243028
private _columnGroups = false;
3025-
private _autoGeneratedCols = [];
30263029

30273030
private _columnWidth: string;
30283031

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export class IgxGridHeaderComponent implements DoCheck, OnDestroy {
117117

118118
@HostBinding('style.height.rem')
119119
public get height() {
120-
if (!this.grid.hasColumnGroups) {
120+
if (!this.grid.hasColumnGroups || this.grid.isPivot) {
121121
return null;
122122
}
123123

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,13 @@
110110
<igx-grid-column-resizer *ngIf="colResizingService.showResizer"></igx-grid-column-resizer>
111111
<div class="igx-grid__loading-outlet" #igxLoadingOverlayOutlet igxOverlayOutlet></div>
112112
<div class="igx-grid__outlet" #igxFilteringOverlayOutlet igxOverlayOutlet></div>
113+
114+
<ng-template #headerTemplate let-column>
115+
<div class="igx-grid__tr--header">
116+
<igx-icon [attr.draggable]="false"
117+
(click)="toggleColumn(column)"
118+
style='cursor: pointer;'>
119+
{{columnGroupStates.get(column) ? 'expand_more' : 'expand_less'}}</igx-icon>
120+
{{column.header}}
121+
</div>
122+
</ng-template>

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

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ import { IgxGridCRUDService } from '../common/crud.service';
1919
import { IgxGridSummaryService } from '../summaries/grid-summary.service';
2020
import { IPivotConfiguration, IPivotDimension } from './pivot-grid.interface';
2121
import { IgxPivotHeaderRowComponent } from './pivot-header-row.component';
22+
import { IgxColumnGroupComponent } from '../columns/column-group.component';
23+
import { IgxColumnComponent } from '../columns/column.component';
2224

2325
let NEXT_ID = 0;
24-
const MINIMUM_COLUMN_WIDTH = 136;
26+
const MINIMUM_COLUMN_WIDTH = 200;
2527
@Component({
2628
changeDetection: ChangeDetectionStrategy.OnPush,
2729
preserveWhitespaces: false,
@@ -43,6 +45,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
4345
GridType {
4446

4547

48+
4649
/** @hidden @internal */
4750
@ViewChild(IgxPivotHeaderRowComponent, { static: true })
4851
public theadRow: IgxPivotHeaderRowComponent;
@@ -70,6 +73,15 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
7073
@ViewChild('record_template', { read: TemplateRef, static: true })
7174
public recordTemplate: TemplateRef<any>;
7275

76+
/**
77+
* @hidden @internal
78+
*/
79+
@ViewChild('headerTemplate', { read: TemplateRef, static: true })
80+
public headerTemplate: TemplateRef<any>;
81+
82+
83+
public columnGroupStates = new Map<IgxColumnGroupComponent, boolean>();
84+
public isPivot = true;
7385
private _data;
7486
private _filteredData;
7587
private p_id = `igx-pivot-grid-${NEXT_ID++}`;
@@ -206,25 +218,85 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
206218
return MINIMUM_COLUMN_WIDTH * rowDimCount;
207219
}
208220

209-
// TODO: should work on original data, before pipes.
210-
// That should be data, but for this poc since we have no pipes use a different prop.
211-
protected generateDataFields(data: any[]): string[] {
212-
let fields = new Map<string, string>();
221+
public toggleColumn(col: IgxColumnComponent) {
222+
const state = this.columnGroupStates.get(col);
223+
this.columnGroupStates.set(col, !state);
224+
}
225+
226+
/**
227+
* @hidden
228+
*/
229+
protected autogenerateColumns() {
230+
const data = this.gridAPI.get_data();
231+
const fieldsMap = this.getFieldsHierarchy(data);
232+
const columns = this.generateColumnHierarchy(fieldsMap, data);
233+
this._autoGeneratedCols = columns;
234+
235+
this.columnList.reset(columns);
236+
if (data && data.length > 0) {
237+
this.shouldGenerate = false;
238+
}
239+
}
240+
241+
protected generateColumnHierarchy(fields: Map<string, any>, data, parent = null): IgxColumnComponent[] {
242+
const factoryColumn = this.resolver.resolveComponentFactory(IgxColumnComponent);
243+
const factoryColumnGroup = this.resolver.resolveComponentFactory(IgxColumnGroupComponent);
244+
let columns = [];
245+
fields.forEach((value, key) => {
246+
if (value.children == null || value.children.size === 0) {
247+
const ref = factoryColumn.create(this.viewRef.injector);
248+
ref.instance.header = key;
249+
ref.instance.field = key;
250+
ref.instance.dataType = this.resolveDataTypes(data[0][key]);
251+
ref.instance.parent = parent;
252+
ref.changeDetectorRef.detectChanges();
253+
columns.push(ref.instance);
254+
} else {
255+
const ref = factoryColumnGroup.create(this.viewRef.injector);
256+
ref.instance.header = key;
257+
ref.instance.headerTemplate = this.headerTemplate;
258+
const children = this.generateColumnHierarchy(value.children, data, ref.instance);
259+
ref.changeDetectorRef.detectChanges();
260+
ref.instance.children.reset(children);
261+
columns.push(ref.instance);
262+
columns = columns.concat(children);
263+
}
264+
});
265+
return columns;
266+
}
267+
268+
protected getFieldsHierarchy(data){
269+
const hierarchy = new Map<string, any>();
213270
for (const rec of this.originalData) {
214271
const vals = this.extractValuesFromDimension(this.pivotConfiguration.columns, rec);
215272
for (const val of vals) {
216-
fields = fields.set(val, val);
273+
if (hierarchy.get(val.value) != null && val.children) {
274+
for (const child of val.children) {
275+
hierarchy.get(val.value).children.set(child.value, child);
276+
}
277+
} else {
278+
hierarchy.set(val.value, val);
279+
hierarchy.get(val.value).children = new Map<string, any>();
280+
for (const child of val.children) {
281+
hierarchy.get(val.value).children.set(child.value, child);
282+
}
283+
}
217284
}
218285
}
219-
const keys = Array.from(fields.keys());
220-
return keys;
286+
return hierarchy;
221287
}
222288

223289
private extractValuesFromDimension(dims: IPivotDimension[], recData: any){
224290
const vals = [];
291+
let i = 0;
225292
for (const col of dims) {
226293
const value = typeof col.member === 'string' ? recData[col.member] : col.member.call(this, recData);
227-
vals.push(value);
294+
vals.push({ value });
295+
if (col.childLevels != null && col.childLevels.length > 0) {
296+
const childValues = this.extractValuesFromDimension(col.childLevels, recData);
297+
vals[i].children = childValues;
298+
}
299+
i++;
228300
}
229301
return vals;
230302
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,15 @@
9494
[cellSelectionMode]="grid.cellSelection"
9595
[displayPinnedChip]="shouldDisplayPinnedChip(col.visibleIndex)">
9696
</igx-grid-cell>
97+
</ng-template>
98+
99+
100+
<ng-template #headerTemplate let-column>
101+
<div class="igx-grid__tr--header igx-grid__row-indentation--level-{{ level }}">
102+
<igx-icon *ngIf="hasChild" [attr.draggable]="false"
103+
(click)="grid.toggleRow(rowID)"
104+
style='cursor: pointer;'>
105+
{{grid.expansionStates.get(rowID) ? 'expand_more' : 'expand_less'}}</igx-icon>
106+
{{column.header}}
107+
</div>
97108
</ng-template>

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

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@ import {
66
ElementRef,
77
forwardRef,
88
OnInit,
9+
TemplateRef,
10+
ViewChild,
911
ViewContainerRef
1012
} from '@angular/core';
1113
import { IgxPivotGridComponent } from './pivot-grid.component';
1214
import { IgxRowDirective } from '../row.directive';
1315
import { GridBaseAPIService, IgxColumnComponent } from '../hierarchical-grid/public_api';
1416
import { IgxGridSelectionService } from '../selection/selection.service';
17+
import { IPivotDimension } from './pivot-grid.interface';
1518

1619

17-
const MINIMUM_COLUMN_WIDTH = 136;
20+
const MINIMUM_COLUMN_WIDTH = 200;
1821
@Component({
1922
changeDetection: ChangeDetectionStrategy.OnPush,
2023
selector: 'igx-pivot-row',
@@ -23,8 +26,15 @@ const MINIMUM_COLUMN_WIDTH = 136;
2326
})
2427
export class IgxPivotRowComponent extends IgxRowDirective<IgxPivotGridComponent> implements OnInit {
2528

29+
/**
30+
* @hidden @internal
31+
*/
32+
@ViewChild('headerTemplate', { read: TemplateRef, static: true })
33+
public headerTemplate: TemplateRef<any>;
2634

2735
public rowDimension: IgxColumnComponent[] = [];
36+
public level = 0;
37+
public hasChild = false;
2838

2939
constructor(
3040
public gridAPI: GridBaseAPIService<IgxPivotGridComponent>,
@@ -36,7 +46,6 @@ export class IgxPivotRowComponent extends IgxRowDirective<IgxPivotGridComponent>
3646
){
3747
super(gridAPI, selectionService, element, cdr);
3848
}
39-
4049
/**
4150
* @hidden
4251
* @internal
@@ -48,24 +57,41 @@ export class IgxPivotRowComponent extends IgxRowDirective<IgxPivotGridComponent>
4857
public ngOnInit() {
4958
// generate rowDimension
5059
const rowDimConfig = this.grid.pivotConfiguration.rows;
51-
let field = null;
60+
this.level = this.rowData['level'] || 0;
61+
this.hasChild = this.rowData['records'] != null && this.rowData['records'].length > 0;
62+
this.extractFromDimensions(rowDimConfig, 0);
63+
}
64+
65+
protected extractFromDimensions(rowDimConfig: IPivotDimension[], level: number){
5266
for (const dim of rowDimConfig) {
53-
if (typeof dim.member === 'string') {
54-
field = this.rowData[dim.member];
55-
} else if (typeof dim.member === 'function'){
56-
field = dim.member.call(this, this.rowData);
67+
if (level === this.level) {
68+
this.rowDimension.push(this.extractFromDimension(dim));
69+
} else {
70+
level++;
71+
this.extractFromDimensions(dim.childLevels, level);
5772
}
58-
const col = this._createColComponent(field);
59-
this.rowDimension.push(col);
6073
}
6174
}
6275

76+
protected extractFromDimension(dim: IPivotDimension) {
77+
let field = null;
78+
if (typeof dim.member === 'string') {
79+
field = this.rowData[dim.member];
80+
} else if (typeof dim.member === 'function'){
81+
field = dim.member.call(this, this.rowData);
82+
}
83+
const col = this._createColComponent(field);
84+
return col;
85+
}
86+
6387
protected _createColComponent(field: string) {
6488
const factoryColumn = this.resolver.resolveComponentFactory(IgxColumnComponent);
6589
const ref = this.viewRef.createComponent(factoryColumn, null, this.viewRef.injector);
6690
ref.instance.field = field;
91+
ref.instance.header = field;
6792
ref.instance.width = MINIMUM_COLUMN_WIDTH + 'px';
6893
(ref as any).instance._vIndex = this.grid.columns.length + this.index;
94+
ref.instance.headerTemplate = this.headerTemplate;
6995
return ref.instance;
7096
}
7197
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<div class="sample-column">
2-
<igx-pivot-grid #grid1 [data]="data" [originalData]="origData" [pivotConfiguration]="pivotConfig">
2+
<igx-pivot-grid #grid1 [data]="dataHierarchical" [originalData]="origData" [pivotConfiguration]="pivotConfigHierarchy">
33
</igx-pivot-grid>
44
</div>

src/app/pivot-grid/pivot-grid.sample.ts

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,37 @@ export class PivotGridSampleComponent {
3333
filters: null
3434
};
3535

36+
public pivotConfigHierarchy: IPivotConfiguration = {
37+
columns: [{
38+
member: () => 'All',
39+
enabled: true,
40+
childLevels: [{
41+
member: 'Country',
42+
enabled: true,
43+
childLevels:[]
44+
}]
45+
}],
46+
rows: [{
47+
member: () => 'All',
48+
enabled: true,
49+
childLevels:[
50+
{
51+
member: (data) => data.ProductCategory,
52+
enabled: true,
53+
childLevels:[]
54+
}
55+
]
56+
}],
57+
values: [
58+
{
59+
member: 'UnitsSold',
60+
aggregate: IgxNumberSummaryOperand.sum,
61+
enabled: true
62+
}
63+
],
64+
filters: null
65+
};
66+
3667
public origData = [
3768
{ ProductCategory: 'Clothing', UnitPrice: 12.81, SellerName: 'Stanley', Country: 'Bulgaria', Date: '01/01/2021', UnitsSold: 282 },
3869
{ ProductCategory: 'Clothing', UnitPrice: 49.57, SellerName: 'Elisa', Country: 'USA', Date: '01/05/2019', UnitsSold: 296 },
@@ -52,10 +83,15 @@ export class PivotGridSampleComponent {
5283
];
5384

5485
public dataHierarchical = [
55-
{ ProductCategory: 'All', Bulgaria: 774, USA: 829, Uruguay: 524 },
56-
{ ProductCategory: 'Clothing', Bulgaria: 774, USA: 296, Uruguay: 456 },
57-
{ ProductCategory: 'Bikes', Uruguay: 68 },
58-
{ ProductCategory: 'Accessories', USA: 293 },
59-
{ ProductCategory: 'Components', USA: 240 }
86+
{ ProductCategory: 'All', All: 1000, Bulgaria: 774, USA: 829, Uruguay: 524, level: 0, records: [
87+
{ ProductCategory: 'Clothing', Bulgaria: 774, USA: 296, Uruguay: 456, level: 1 },
88+
{ ProductCategory: 'Bikes', Uruguay: 68, level: 1 },
89+
{ ProductCategory: 'Accessories', USA: 293, level: 1 },
90+
{ ProductCategory: 'Components', USA: 240, level: 1 }
91+
] },
92+
{ ProductCategory: 'Clothing', All: 1000, Bulgaria: 774, USA: 296, Uruguay: 456, level: 1 },
93+
{ ProductCategory: 'Bikes', All: 1000, Uruguay: 68, level: 1 },
94+
{ ProductCategory: 'Accessories', All: 1000, USA: 293, level: 1 },
95+
{ ProductCategory: 'Components', All: 1000, USA: 240, level: 1 }
6096
];
6197
}

0 commit comments

Comments
 (0)