Skip to content

Commit 60e8c63

Browse files
authored
Merge pull request #10224 from IgniteUI/mkirova/pivot-pipe-multiple-col-dimensions
Add handling for multiple column dimensions.
2 parents d4e9e6d + f2c4bcc commit 60e8c63

File tree

6 files changed

+194
-84
lines changed

6 files changed

+194
-84
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
<igx-icon [attr.draggable]="false"
118118
(click)="toggleColumn(column)"
119119
style='cursor: pointer;'>
120-
{{columnGroupStates.get(column) ? 'expand_more' : 'expand_less'}}</igx-icon>
120+
{{columnGroupStates.get(column.field) ? 'expand_more' : 'expand_less'}}</igx-icon>
121121
{{column.header}}
122122
</div>
123123
</ng-template>

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

Lines changed: 61 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { IPivotConfiguration, IPivotDimension } from './pivot-grid.interface';
2121
import { IgxPivotHeaderRowComponent } from './pivot-header-row.component';
2222
import { IgxColumnGroupComponent } from '../columns/column-group.component';
2323
import { IgxColumnComponent } from '../columns/column.component';
24+
import { PivotUtil } from './pivot-util';
2425

2526
let NEXT_ID = 0;
2627
const MINIMUM_COLUMN_WIDTH = 200;
@@ -80,7 +81,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
8081
public headerTemplate: TemplateRef<any>;
8182

8283

83-
public columnGroupStates = new Map<IgxColumnGroupComponent, boolean>();
84+
public columnGroupStates = new Map<string, boolean>();
8485
public isPivot = true;
8586
protected _defaultExpandState = true;
8687
private _data;
@@ -196,16 +197,54 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
196197
}
197198

198199
public toggleColumn(col: IgxColumnComponent) {
199-
const state = this.columnGroupStates.get(col);
200-
this.columnGroupStates.set(col, !state);
200+
const state = this.columnGroupStates.get(col.field);
201+
const newState = !state;
202+
this.columnGroupStates.set(col.field, newState);
203+
const parentCols = col.parent ? col.parent.children : this.columns.filter(x => x.level === 0);
204+
const fieldColumn = parentCols.filter(x => x.header === col.header && !x.columnGroup)[0];
205+
const groupColumn = parentCols.filter(x => x.header === col.header && x.columnGroup)[0];
206+
groupColumn.hidden = newState;
207+
this.resolveToggle(groupColumn);
208+
fieldColumn.hidden = !newState;
209+
if (newState) {
210+
fieldColumn.headerTemplate = this.headerTemplate;
211+
} else {
212+
fieldColumn.headerTemplate = undefined;
213+
}
214+
this.reflow();
215+
}
216+
217+
protected resolveToggle(groupColumn: IgxColumnComponent) {
218+
const hasChildGroup = groupColumn.children.filter(x => x.columnGroup).length > 0;
219+
if (!groupColumn.hidden && hasChildGroup) {
220+
const fieldChildren = groupColumn.children.filter(x => !x.columnGroup);
221+
const groupChildren = groupColumn.children.filter(x => x.columnGroup);
222+
groupChildren.forEach(group => {
223+
this.resolveToggle(group);
224+
});
225+
fieldChildren.forEach(fieldChild => {
226+
fieldChild.hidden = true;
227+
});
228+
}
201229
}
202230

231+
/**
232+
* @hidden
233+
* @internal
234+
*/
235+
protected calcGridHeadRow() {
236+
}
237+
203238
/**
204239
* @hidden
205240
*/
206241
protected autogenerateColumns() {
207242
const data = this.gridAPI.get_data();
208-
const fieldsMap = this.getFieldsHierarchy(data);
243+
const fieldsMap = PivotUtil.getFieldsHierarchy(
244+
data,
245+
this.pivotConfiguration.columns,
246+
{aggregations: 'aggregations', records: 'records', children: 'children', level: 'level'}
247+
);
209248
const columns = this.generateColumnHierarchy(fieldsMap, data);
210249
this._autoGeneratedCols = columns;
211250

@@ -220,33 +259,34 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
220259
const factoryColumnGroup = this.resolver.resolveComponentFactory(IgxColumnGroupComponent);
221260
let columns = [];
222261
fields.forEach((value, key) => {
223-
if (value.children == null || value.children.size === 0) {
262+
if (value.children == null || value.children.length === 0 || value.children.size === 0) {
224263
const ref = factoryColumn.create(this.viewRef.injector);
225-
ref.instance.header = key;
264+
ref.instance.header = parent != null ? key.split(parent.header + '-')[1] : key;
226265
ref.instance.field = key;
227266
ref.instance.dataType = this.resolveDataTypes(data[0][key]);
228267
ref.instance.parent = parent;
229-
ref.instance.visibleWhenCollapsed = false;
230268
ref.changeDetectorRef.detectChanges();
231269
columns.push(ref.instance);
232270
} else {
233271
const ref = factoryColumnGroup.create(this.viewRef.injector);
234-
ref.instance.header = key;
235-
//ref.instance.headerTemplate = this.headerTemplate;
272+
ref.instance.parent = parent;
273+
ref.instance.field = key;
274+
ref.instance.header = parent != null ? key.split(parent.header + '-')[1] : key;
275+
if (value.expandable) {
276+
ref.instance.headerTemplate = this.headerTemplate;
277+
const refSibling = factoryColumn.create(this.viewRef.injector);
278+
refSibling.instance.header = parent != null ? key.split(parent.header + '-')[1] : key;
279+
refSibling.instance.field = key;
280+
refSibling.instance.dataType = this.resolveDataTypes(data[0][key]);
281+
refSibling.instance.parent = parent;
282+
refSibling.instance.hidden = true;
283+
columns.push(refSibling.instance);
284+
}
236285
const children = this.generateColumnHierarchy(value.children, data, ref.instance);
237-
238-
const refChild = factoryColumn.create(this.viewRef.injector);
239-
refChild.instance.header = key;
240-
refChild.instance.field = key;
241-
refChild.instance.dataType = this.resolveDataTypes(data[0][key]);
242-
refChild.instance.parent = ref.instance;
243-
refChild.instance.visibleWhenCollapsed = true;
244-
245-
children.push(refChild.instance);
246-
286+
const filteredChildren = children.filter(x => x.level === ref.instance.level + 1);
247287
ref.changeDetectorRef.detectChanges();
248-
ref.instance.children.reset(children);
249-
ref.instance.collapsible = true;
288+
289+
ref.instance.children.reset(filteredChildren);
250290

251291
columns.push(ref.instance);
252292
columns = columns.concat(children);
@@ -255,48 +295,5 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
255295
return columns;
256296
}
257297

258-
protected getFieldsHierarchy(data){
259-
const hierarchy = new Map<string, any>();
260-
for (const rec of data) {
261-
const vals = this.extractValuesFromDimension(this.pivotConfiguration.columns, rec);
262-
for (const val of vals) {
263-
if (hierarchy.get(val.value) != null && val.children) {
264-
for (const child of val.children) {
265-
hierarchy.get(val.value).children.set(child.value, child);
266-
}
267-
} else {
268-
const children = val.children;
269-
hierarchy.set(val.value, val);
270-
hierarchy.get(val.value).children = new Map<string, any>();
271-
for (const child of children) {
272-
hierarchy.get(val.value).children.set(child.value, child);
273-
}
274-
}
275-
}
276-
}
277-
return hierarchy;
278-
}
279298

280-
private extractValuesFromDimension(dims: IPivotDimension[], recData: any){
281-
const vals = [];
282-
let i = 0;
283-
for (const col of dims) {
284-
const value = typeof col.member === 'string' ? recData[col.member] : col.member.call(this, recData);
285-
if (vals.length > 0) {
286-
const newChildVal = vals[0].value + '-' + value;
287-
if(!vals[0].children) {
288-
vals[0].children = [];
289-
}
290-
vals[0].children.push({ value });
291-
} else {
292-
vals.push({ value });
293-
}
294-
if (col.childLevels != null && col.childLevels.length > 0) {
295-
const childValues = this.extractValuesFromDimension(col.childLevels, recData);
296-
vals[i].children = childValues;
297-
}
298-
i++;
299-
}
300-
return vals;
301-
}
302299
}

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,4 +275,74 @@ describe('Pivot pipes', () => {
275275
]);
276276
});
277277

278+
it('transforms flat data to pivot data 2 column dimensions', () => {
279+
const rowPipeResult = rowPipe.transform(data, pivotConfigHierarchy.rows, pivotConfigHierarchy.values);
280+
const columnPipeResult = columnPipe.transform(rowPipeResult, [{
281+
member: 'Country',
282+
enabled: true,
283+
childLevels:[]
284+
},
285+
{
286+
member: 'Date',
287+
enabled: true,
288+
childLevels: []
289+
}], pivotConfigHierarchy.values);
290+
/* eslint-disable quote-props */
291+
expect(columnPipeResult).toEqual([
292+
{'field1':'All','records':[
293+
{'field1':'Clothing','level':1,'Bulgaria':774,'Bulgaria-01/01/2021':282,
294+
'Bulgaria-02/19/2020':492,'USA':296,'USA-01/05/2019':296,'Uruguay':456,'Uruguay-05/12/2020':456},
295+
{'field1':'Bikes','level':1,'Uruguay':68,'Uruguay-01/06/2020':68},
296+
{'field1':'Accessories','level':1,'USA':293,'USA-04/07/2021':293},
297+
{'field1':'Components','level':1,'USA':240,'USA-12/08/2021':240}]
298+
,'level':0,'Bulgaria':774,'Bulgaria-01/01/2021':282,'Bulgaria-02/19/2020':492,
299+
'USA':829,'USA-01/05/2019':296,'USA-04/07/2021':293,'USA-12/08/2021':240,
300+
'Uruguay':524,'Uruguay-05/12/2020':456,'Uruguay-01/06/2020':68},
301+
{'field1':'Clothing','level':1,'Bulgaria':774,'Bulgaria-01/01/2021':282,
302+
'Bulgaria-02/19/2020':492,'USA':296,'USA-01/05/2019':296,'Uruguay':456,'Uruguay-05/12/2020':456},
303+
{'field1':'Bikes','level':1,'Uruguay':68,'Uruguay-01/06/2020':68},
304+
{'field1':'Accessories','level':1,'USA':293,'USA-04/07/2021':293},
305+
{'field1':'Components','level':1,'USA':240,'USA-12/08/2021':240}]);
306+
});
307+
308+
it('transforms flat data to pivot data 3 column dimensions', () => {
309+
const rowPipeResult = rowPipe.transform(data, pivotConfigHierarchy.rows, pivotConfigHierarchy.values);
310+
const columnPipeResult = columnPipe.transform(rowPipeResult, [{
311+
member: 'Country',
312+
enabled: true,
313+
childLevels:[]
314+
},
315+
{
316+
member: 'SellerName',
317+
enabled: true,
318+
childLevels:[]
319+
},
320+
{
321+
member: 'Date',
322+
enabled: true,
323+
childLevels: []
324+
}], pivotConfigHierarchy.values);
325+
/* eslint-disable quote-props */
326+
expect(columnPipeResult).toEqual([
327+
{'field1':'All','records':
328+
[
329+
{'field1':'Clothing','level':1,'Bulgaria':774,'Bulgaria-Stanley':282,'Bulgaria-Stanley-01/01/2021':282,
330+
'Bulgaria-Walter':492,'Bulgaria-Walter-02/19/2020':492,'USA':296,'USA-Elisa':296,'USA-Elisa-01/05/2019':296,
331+
'Uruguay':456,'Uruguay-Larry':456,'Uruguay-Larry-05/12/2020':456},
332+
{'field1':'Bikes','level':1,'Uruguay':68,'Uruguay-Lydia':68,'Uruguay-Lydia-01/06/2020':68},
333+
{'field1':'Accessories','level':1,'USA':293,'USA-David':293,'USA-David-04/07/2021':293},
334+
{'field1':'Components','level':1,'USA':240,'USA-John':240,'USA-John-12/08/2021':240}
335+
],
336+
'level':0,'Bulgaria':774,'Bulgaria-Stanley':282,'Bulgaria-Stanley-01/01/2021':282,'Bulgaria-Walter':492,
337+
'Bulgaria-Walter-02/19/2020':492,'USA':829,'USA-Elisa':296,'USA-Elisa-01/05/2019':296,'USA-David':293,
338+
'USA-David-04/07/2021':293,'USA-John':240,'USA-John-12/08/2021':240,'Uruguay':524,'Uruguay-Larry':456,
339+
'Uruguay-Larry-05/12/2020':456,'Uruguay-Lydia':68,'Uruguay-Lydia-01/06/2020':68},
340+
{'field1':'Clothing','level':1,'Bulgaria':774,'Bulgaria-Stanley':282,'Bulgaria-Stanley-01/01/2021':282,
341+
'Bulgaria-Walter':492,'Bulgaria-Walter-02/19/2020':492,'USA':296,'USA-Elisa':296,'USA-Elisa-01/05/2019':296,'Uruguay':456,
342+
'Uruguay-Larry':456,'Uruguay-Larry-05/12/2020':456},
343+
{'field1':'Bikes','level':1,'Uruguay':68,'Uruguay-Lydia':68,'Uruguay-Lydia-01/06/2020':68},
344+
{'field1':'Accessories','level':1,'USA':293,'USA-David':293,'USA-David-04/07/2021':293},
345+
{'field1':'Components','level':1,'USA':240,'USA-John':240,'USA-John-12/08/2021':240}
346+
]);
347+
});
278348
});

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,11 @@ export class IgxPivotRowComponent extends IgxRowDirective<IgxPivotGridComponent>
9797
}
9898

9999
protected _createColComponent(field: string) {
100+
const fieldName = field.indexOf('-') !== -1 ? field.slice(field.lastIndexOf('-') + 1) : field;
100101
const factoryColumn = this.resolver.resolveComponentFactory(IgxColumnComponent);
101102
const ref = this.viewRef.createComponent(factoryColumn, null, this.viewRef.injector);
102103
ref.instance.field = field;
103-
ref.instance.header = field;
104+
ref.instance.header = fieldName;
104105
ref.instance.width = MINIMUM_COLUMN_WIDTH + 'px';
105106
(ref as any).instance._vIndex = this.grid.columns.length + this.index;
106107
ref.instance.headerTemplate = this.headerTemplate;

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

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export class PivotUtil {
1212
this.applyHierarchyChildren(hierarchy, val, rec, pivotKeys.records);
1313
} else {
1414
hierarchy.set(val.value, cloneValue(val));
15-
hierarchy.get(val.value).children = new Map<string, any>();
1615
this.applyHierarchyChildren(hierarchy, val, rec, pivotKeys.records);
1716
}
1817
}
@@ -24,25 +23,40 @@ export class PivotUtil {
2423
return typeof dim.member === 'string' ? recData[dim.member] : dim.member.call(null, recData);
2524
}
2625

27-
public static extractValuesFromDimension(dims: IPivotDimension[], recData: any) {
26+
public static extractValuesFromDimension(dims: IPivotDimension[], recData: any, path = []) {
2827
const vals = [];
29-
let i = 0;
30-
for (const col of dims) {
28+
let lvlCollection = vals;
29+
const flattenedDims = this.flatten(dims);
30+
for (const col of flattenedDims) {
3131
const value = this.extractValueFromDimension(col, recData);
32-
vals.push({ value });
33-
if (col.childLevels != null && col.childLevels.length > 0) {
34-
const childValues = this.extractValuesFromDimension(col.childLevels, recData);
35-
vals[i].children = childValues;
32+
path.push(value);
33+
const newValue = path.join('-');
34+
lvlCollection.push({ value: newValue });
35+
lvlCollection[0].expandable = col.expandable;
36+
if (!lvlCollection[0].children) {
37+
lvlCollection[0].children = [];
3638
}
37-
i++;
39+
lvlCollection = lvlCollection[0].children;
3840
}
3941
return vals;
4042
}
4143

44+
public static flatten(arr) {
45+
const newArr = arr.reduce((acc, item) => {
46+
acc.push(item);
47+
if (Array.isArray(item.childLevels) && item.childLevels.length > 0) {
48+
item.expandable = true;
49+
acc = acc.concat(this.flatten(item.childLevels));
50+
}
51+
return acc;
52+
}, []);
53+
return newArr;
54+
}
55+
4256
public static applyAggregations(hierarchies, values, pivotKeys) {
4357
hierarchies.forEach((hierarchy) => {
4458
const children = hierarchy[pivotKeys.children];
45-
if (children) {
59+
if (children && children.size > 0) {
4660
this.applyAggregations(children, values, pivotKeys);
4761
const childrenAggregations = this.collectAggregations(children, pivotKeys);
4862
hierarchy[pivotKeys.aggregations] = this.aggregate(childrenAggregations, values);
@@ -95,13 +109,19 @@ export class PivotUtil {
95109
hierarchies.forEach((h, key) => {
96110
const obj = {};
97111
for (const value of values) {
98-
obj[key] = h[pivotKeys.aggregations][value.member];
112+
if(h[pivotKeys.aggregations]) {
113+
obj[key] = h[pivotKeys.aggregations][value.member];
114+
}
99115
obj[pivotKeys.records] = h[pivotKeys.records];
100116
flatData.push(obj);
101117
if (h[pivotKeys.children]) {
102118
const records = this.flattenColumnHierarchy(h[pivotKeys.children], values, pivotKeys);
103119
for (const record of records) {
104-
flatData.push(record);
120+
delete record[pivotKeys.records];
121+
const childKeys = Object.keys(record);
122+
for (const childKey of childKeys) {
123+
obj[childKey] = record[childKey];
124+
}
105125
}
106126
}
107127
}
@@ -132,14 +152,18 @@ export class PivotUtil {
132152
}
133153

134154
private static applyHierarchyChildren(hierarchy, val, rec, recordsKey) {
135-
if (!val.children) {
155+
const childCollection = val.children;
156+
if (Array.isArray(hierarchy.get(val.value).children)) {
157+
hierarchy.get(val.value).children = new Map<string, any>();
158+
}
159+
if (!childCollection || childCollection.length === 0) {
136160
if (hierarchy.get(val.value)[recordsKey]) {
137161
hierarchy.get(val.value)[recordsKey].push(rec);
138162
} else {
139163
hierarchy.get(val.value)[recordsKey] = [rec];
140164
}
141165
} else {
142-
for (const child of val.children) {
166+
for (const child of childCollection) {
143167
if (!hierarchy.get(val.value).children.get(child.value)) {
144168
hierarchy.get(val.value).children.set(child.value, child);
145169
}
@@ -149,6 +173,10 @@ export class PivotUtil {
149173
} else {
150174
hierarchy.get(val.value).children.get(child.value)[recordsKey] = [rec];
151175
}
176+
177+
if (child.children && child.children.length > 0) {
178+
this.applyHierarchyChildren( hierarchy.get(val.value).children, child, rec, recordsKey);
179+
}
152180
}
153181
}
154182
}

0 commit comments

Comments
 (0)