Skip to content

Commit 01640e8

Browse files
authored
Merge branch '20.1.x' into pmoleri/fix-template-outlet-leak
2 parents 3c9bc9b + 9221089 commit 01640e8

File tree

7 files changed

+124
-26
lines changed

7 files changed

+124
-26
lines changed

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

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@angular/core';
2-
import { first } from 'rxjs/operators';
2+
import { first, throttleTime } from 'rxjs/operators';
33
import { IgxForOfDirective } from '../directives/for-of/for_of.directive';
44
import { GridType } from './common/grid.interface';
55
import {
@@ -17,6 +17,7 @@ import { IActiveNodeChangeEventArgs } from './common/events';
1717
import { IgxGridGroupByRowComponent } from './grid/groupby-row.component';
1818
import { IMultiRowLayoutNode } from './common/types';
1919
import { SortingDirection } from '../data-operations/sorting-strategy';
20+
import { animationFrameScheduler, Subject } from 'rxjs';
2021
export interface ColumnGroupsCache {
2122
level: number;
2223
visibleIndex: number;
@@ -37,6 +38,7 @@ export class IgxGridNavigationService {
3738
public _activeNode: IActiveNode = {} as IActiveNode;
3839
public lastActiveNode: IActiveNode = {} as IActiveNode;
3940
protected pendingNavigation = false;
41+
protected keydownNotify = new Subject<KeyboardEvent>();
4042

4143
public get activeNode() {
4244
return this._activeNode;
@@ -46,7 +48,15 @@ export class IgxGridNavigationService {
4648
this._activeNode = value;
4749
}
4850

49-
constructor(protected platform: PlatformUtil) { }
51+
constructor(protected platform: PlatformUtil) {
52+
this.keydownNotify.pipe(
53+
throttleTime(30, animationFrameScheduler),
54+
)
55+
.subscribe((event: KeyboardEvent) => {
56+
this.dispatchEvent(event);
57+
});
58+
59+
}
5060

5161
public handleNavigation(event: KeyboardEvent) {
5262
const key = event.key.toLowerCase();
@@ -60,7 +70,7 @@ export class IgxGridNavigationService {
6070
event.preventDefault();
6171
}
6272
if (event.repeat) {
63-
setTimeout(() => this.dispatchEvent(event), 1);
73+
this.keydownNotify.next(event);
6474
} else {
6575
this.dispatchEvent(event);
6676
}
@@ -96,10 +106,8 @@ export class IgxGridNavigationService {
96106
event.preventDefault();
97107
this.navigateInBody(position.rowIndex, position.colIndex, (obj) => {
98108
obj.target.activate(event);
99-
this.grid.cdr.detectChanges();
100109
});
101110
}
102-
this.grid.cdr.detectChanges();
103111
}
104112

105113
public summaryNav(event: KeyboardEvent) {
@@ -145,7 +153,6 @@ export class IgxGridNavigationService {
145153
this.grid.clearCellSelection();
146154
this.grid.navigateTo(this.activeNode.row, this.activeNode.column, (obj) => {
147155
obj.target?.activate(event);
148-
this.grid.cdr.detectChanges();
149156
});
150157
} else {
151158
if (hasLastActiveNode && !this.grid.selectionService.selected(this.lastActiveNode)) {
@@ -598,7 +605,6 @@ export class IgxGridNavigationService {
598605

599606
this.navigateInBody(next.rowIndex, next.visibleColumnIndex, (obj) => {
600607
obj.target.activate(event);
601-
this.grid.cdr.detectChanges();
602608
});
603609
}
604610

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,8 @@ describe('IgxGrid - Row Editing #grid', () => {
617617
// let cellDebug;
618618
UIInteractions.simulateDoubleClickAndSelectEvent(cellElem);
619619
fix.detectChanges();
620+
await wait(DEBOUNCETIME);
621+
fix.detectChanges();
620622

621623
UIInteractions.triggerEventHandlerKeyDown('tab', gridContent, false, true);
622624
await wait(DEBOUNCETIME);
@@ -977,11 +979,11 @@ describe('IgxGrid - Row Editing #grid', () => {
977979
fix.detectChanges();
978980

979981
const keyDonwSpy = spyOn(grid.gridKeydown, 'emit');
980-
const detectChangesSpy = spyOn(grid.cdr, 'detectChanges').and.callThrough();
981982

982983
UIInteractions.simulateDoubleClickAndSelectEvent(targetCell);
983984
fix.detectChanges();
984985

986+
const detectChangesSpy = spyOn(grid.cdr, 'detectChanges').and.callThrough();
985987
const cellElem = fix.debugElement.query(By.css(CELL_CLASS));
986988
const input = cellElem.query(By.css('input'));
987989

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,73 @@ describe('IgxTreeGrid - Integration #tGrid', () => {
198198
treeGrid = fix.componentInstance.treeGrid;
199199
});
200200

201+
it('should preserve the order of records on inner levels', () => {
202+
fix = TestBed.createComponent(IgxTreeGridPrimaryForeignKeyComponent);
203+
fix.componentInstance.sortByName = true;
204+
fix.detectChanges();
205+
treeGrid = fix.componentInstance.treeGrid;
206+
207+
const expectedFlatData = [
208+
{
209+
"ID": 1,
210+
"ParentID": -1,
211+
"Name": "Casey Houston",
212+
"JobTitle": "Vice President",
213+
"Age": 32
214+
},
215+
{
216+
"ID": 2,
217+
"ParentID": 1,
218+
"Name": "Gilberto Todd",
219+
"JobTitle": "Director",
220+
"Age": 41
221+
},
222+
{
223+
"ID": 7,
224+
"ParentID": 2,
225+
"Name": "Debra Morton",
226+
"JobTitle": "Associate Software Developer",
227+
"Age": 35
228+
},
229+
{
230+
"ID": 3,
231+
"ParentID": 2,
232+
"Name": "Tanya Bennett",
233+
"JobTitle": "Director",
234+
"Age": 29
235+
},
236+
{
237+
"ID": 4,
238+
"ParentID": 1,
239+
"Name": "Jack Simon",
240+
"JobTitle": "Software Developer",
241+
"Age": 33
242+
},
243+
{
244+
"ID": 10,
245+
"ParentID": -1,
246+
"Name": "Eduardo Ramirez",
247+
"JobTitle": "Manager",
248+
"Age": 53
249+
},
250+
{
251+
"ID": 9,
252+
"ParentID": 10,
253+
"Name": "Leslie Hansen",
254+
"JobTitle": "Associate Software Developer",
255+
"Age": 44
256+
},
257+
{
258+
"ID": 6,
259+
"ParentID": -1,
260+
"Name": "Erma Walsh",
261+
"JobTitle": "CEO",
262+
"Age": 52
263+
},
264+
]
265+
expect(treeGrid.flatData).toEqual(expectedFlatData);
266+
});
267+
201268
it('should transform a non-tree column into a tree column when pinning it', () => {
202269
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5);
203270

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

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,30 +53,44 @@ export class IgxTreeGridHierarchizingPipe implements PipeTransform {
5353
return primaryKey ? rowData[primaryKey] : rowData;
5454
}
5555

56-
private hierarchizeFlatData(collection: any[], primaryKey: string, foreignKey: string,
57-
map: Map<any, ITreeGridRecord>, flatData: any[]):
58-
ITreeGridRecord[] {
59-
const result: ITreeGridRecord[] = [];
60-
const missingParentRecords: ITreeGridRecord[] = [];
56+
/**
57+
* Converts a flat array of data into a hierarchical (tree) structure,
58+
* preserving the original order of the records among siblings.
59+
*
60+
* It uses a two-pass approach:
61+
* 1. Creates all ITreeGridRecord objects and populates the Map for quick lookup.
62+
* 2. Links the records by iterating again, ensuring children are added to
63+
* their parent's children array in the order they appeared in the
64+
* original collection.
65+
*
66+
* @param collection The flat array of data to be hierarchized. This is the array whose order should be preserved.
67+
* @param primaryKey The name of the property in the data objects that serves as the unique identifier (e.g., 'id').
68+
* @param foreignKey The name of the property in the data objects that links to the parent's primary key (e.g., 'parentId').
69+
* @param map A pre-existing Map object (key: primaryKey value, value: ITreeGridRecord) used to store and quickly look up all created records.
70+
* @param flatData The original flat data array. Used for passing to the setIndentationLevels method (not directly used for hierarchy building).
71+
* @returns An array of ITreeGridRecord objects representing the root nodes of the hierarchy, ordered as they appeared in the original collection.
72+
*/
73+
private hierarchizeFlatData(
74+
collection: any[],
75+
primaryKey: string,
76+
foreignKey: string,
77+
map: Map<any, ITreeGridRecord>,
78+
flatData: any[]
79+
): ITreeGridRecord[] {
6180
collection.forEach(row => {
6281
const record: ITreeGridRecord = {
6382
key: this.getRowID(primaryKey, row),
6483
data: row,
6584
children: []
6685
};
67-
const parent = map.get(row[foreignKey]);
68-
if (parent) {
69-
record.parent = parent;
70-
parent.children.push(record);
71-
} else {
72-
missingParentRecords.push(record);
73-
}
74-
7586
map.set(row[primaryKey], record);
7687
});
7788

78-
missingParentRecords.forEach(record => {
79-
const parent = map.get(record.data[foreignKey]);
89+
const result: ITreeGridRecord[] = [];
90+
collection.forEach(row => {
91+
const record: ITreeGridRecord = map.get(row[primaryKey])!;
92+
const parent = map.get(row[foreignKey]);
93+
8094
if (parent) {
8195
record.parent = parent;
8296
parent.children.push(record);

projects/igniteui-angular/src/lib/test-utils/helper-utils.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export let gridsubscriptions: Subscription [] = [];
2828
export const setupGridScrollDetection = (fixture: ComponentFixture<any>, grid: GridType) => {
2929
gridsubscriptions.push(grid.verticalScrollContainer.chunkLoad.subscribe(() => fixture.detectChanges()));
3030
gridsubscriptions.push(grid.parentVirtDir.chunkLoad.subscribe(() => fixture.detectChanges()));
31+
gridsubscriptions.push(grid.activeNodeChange.subscribe(() => grid.cdr.detectChanges()));
32+
gridsubscriptions.push(grid.selected.subscribe(() => grid.cdr.detectChanges()));
3133
};
3234

3335
export const setupHierarchicalGridScrollDetection = (fixture: ComponentFixture<any>, hierarchicalGrid: IgxHierarchicalGridComponent) => {

projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,17 @@ export class IgxTreeGridWithNoScrollsComponent {
159159
`,
160160
imports: [IgxTreeGridComponent, IgxColumnComponent, IgxPaginatorComponent]
161161
})
162-
export class IgxTreeGridPrimaryForeignKeyComponent {
162+
export class IgxTreeGridPrimaryForeignKeyComponent implements OnInit {
163163
@ViewChild(IgxTreeGridComponent, { static: true }) public treeGrid: IgxTreeGridComponent;
164-
public data = SampleTestData.employeePrimaryForeignKeyTreeData();
164+
public data = [];
165165
public paging = false;
166+
public sortByName = false;
167+
168+
public ngOnInit(): void {
169+
this.data = !this.sortByName
170+
? SampleTestData.employeePrimaryForeignKeyTreeData()
171+
: SampleTestData.employeePrimaryForeignKeyTreeData().sort((a, b) => a.Name.localeCompare(b.Name));
172+
}
166173
}
167174

168175
@Component({

src/app/grid-performance/grid-performance.sample.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ <h4 class="sample-title"> Fixed Size Rows</h4>
88
</igx-grid-toolbar-actions>
99
</igx-grid-toolbar>
1010
@for (c of columns; track c) {
11-
<igx-column width='auto' [sortable]="true" [filterable]="true" [resizable]="true" [field]="c.field" [header]="c.field">
11+
<igx-column width='auto' [editable]="true" [sortable]="true" [filterable]="true" [resizable]="true" [field]="c.field" [header]="c.field">
1212
</igx-column>
1313
}
1414
</igx-grid>

0 commit comments

Comments
 (0)