From de55ef5ead3d26eddd659942c126841a57c07181 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 3 Apr 2019 18:51:28 +0300 Subject: [PATCH 1/3] fix(HierarchicalGrid): In case child grid row was cached (detached from view) and was not destroyed, manually destroy it on root grid ngOnDestroy. --- .../child-grid-row.component.ts | 1 + .../hierarchical-grid-base.component.ts | 6 +++++ .../hierarchical-grid.component.ts | 16 ++++++++++-- .../hierarchical-grid.spec.ts | 26 +++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/child-grid-row.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/child-grid-row.component.ts index baac99731f3..dd64aedb779 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/child-grid-row.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/child-grid-row.component.ts @@ -152,6 +152,7 @@ export class IgxChildGridRowComponent implements AfterViewInit, OnInit { }); this.hGrid.parent = this.parentGrid; this.hGrid.parentIsland = this.layout; + this.hGrid.childRow = this; this.layout.onGridCreated.emit({ owner: this.layout, parentID: this.rowData.rowID, diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.component.ts index 080220daa94..d3a82ce1033 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.component.ts @@ -24,6 +24,7 @@ import { DOCUMENT } from '@angular/common'; import { IgxHierarchicalSelectionAPIService } from './selection'; import { IgxHierarchicalGridNavigationService } from './hierarchical-grid-navigation.service'; import { IgxGridSummaryService } from '../summaries/grid-summary.service'; +import { IgxChildGridRowComponent } from './child-grid-row.component'; export const IgxHierarchicalTransactionServiceFactory = { provide: IgxGridTransaction, @@ -76,6 +77,11 @@ export abstract class IgxHierarchicalGridBaseComponent extends IgxGridBaseCompon */ public parentIsland: IgxRowIslandComponent; + /** + * @hidden + */ + public childRow: IgxChildGridRowComponent; + protected _expandChildren = false; constructor( diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts index 85bfacc9eb8..b4a975842fa 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts @@ -19,7 +19,8 @@ import { AfterViewInit, AfterContentInit, Optional, - OnInit + OnInit, + OnDestroy } from '@angular/core'; import { IgxGridBaseComponent, IgxGridTransaction } from '../grid-base.component'; import { GridBaseAPIService } from '../api.service'; @@ -59,7 +60,7 @@ export interface HierarchicalStateRecord { ] }) export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseComponent - implements IGridDataBindable, AfterViewInit, AfterContentInit, OnInit { + implements IGridDataBindable, AfterViewInit, AfterContentInit, OnInit, OnDestroy { private _overlayIDs = []; /** * Sets the value of the `id` attribute. If not provided it will be automatically generated. @@ -406,6 +407,17 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseCompone super.ngAfterContentInit(); } + ngOnDestroy() { + if (!this.parent) { + this.hgridAPI.getChildGrids(true).forEach((grid) => { + if (!grid.childRow.cdr.destroyed) { + grid.childRow.cdr.destroy(); + } + }); + } + super.ngOnDestroy(); + } + /** * @hidden */ diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.spec.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.spec.ts index 1aa19bbca1b..c45a607e991 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.spec.ts @@ -463,6 +463,32 @@ describe('IgxHierarchicalGrid Row Islands', () => { hVirt = childGrid.getRowByIndex(0).virtDirRow; expect(hVirt.getHorizontalScroll().scrollWidth).toBe(272); }); + + it('should destroy cached instances of child grids when root grid is destroyed', (async () => { + const row = hierarchicalGrid.getRowByIndex(0) as IgxHierarchicalRowComponent; + UIInteractions.clickElement(row.expander); + fixture.detectChanges(); + + const children = hierarchicalGrid.hgridAPI.getChildGrids(true); + expect(children.length).toBe(2); + const child1 = children[0]; + const child2 = children[1]; + expect(child1._destroyed).toBeFalsy(); + expect(child2._destroyed).toBeFalsy(); + hierarchicalGrid.verticalScrollContainer.scrollTo(hierarchicalGrid.verticalScrollContainer.igxForOf.length - 1); + await wait(100); + fixture.detectChanges(); + + // check that we have child is not destroyed + expect(child1._destroyed).toBeFalsy(); + expect(child2._destroyed).toBeFalsy(); + + // destroy hgrid + fixture.destroy(); + + expect(child1._destroyed).toBeTruthy(); + expect(child2._destroyed).toBeTruthy(); + })); }); describe('IgxHierarchicalGrid Remote Scenarios', () => { From 1d9c979adcd1900305512905c83de39ecf87bba5 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 3 Apr 2019 19:44:00 +0300 Subject: [PATCH 2/3] chore(*): Making sure that reflow is not called after component is destroyed as a result of being called in requestAnimationFrame. --- .../src/lib/grids/grid-base.component.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.component.ts b/projects/igniteui-angular/src/lib/grids/grid-base.component.ts index be04feee6f1..b0ecedc66cf 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.component.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.component.ts @@ -556,8 +556,10 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements if (this._height !== value) { this._height = value; requestAnimationFrame(() => { - this.reflow(); - this.cdr.markForCheck(); + if (!this._destroyed) { + this.reflow(); + this.cdr.markForCheck(); + } }); } } @@ -590,7 +592,9 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements // Calling reflow(), because the width calculation // might make the horizontal scrollbar appear/disappear. // This will change the height, which should be recalculated. - this.reflow(); + if (!this._destroyed) { + this.reflow(); + } }); } } From 54d6bbbf48b46099a7c105e25b1900231f09b963 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 4 Apr 2019 13:37:33 +0300 Subject: [PATCH 3/3] test(HierarchicalGrid): Re-adding and updating tests that were previously failing due to #4440. --- .../hierarchical-grid.navigation.spec.ts | 8 +----- .../hierarchical-grid.virtualization.spec.ts | 27 +++++++++---------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.navigation.spec.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.navigation.spec.ts index f34087c34ef..7054e1869ac 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.navigation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.navigation.spec.ts @@ -744,7 +744,7 @@ describe('IgxHierarchicalGrid Complex Navigation', () => { expect(nextCell.rowIndex).toBe(2); })); - xit('should allow navigating up from parent into nested child grid', (async () => { + it('should allow navigating up from parent into nested child grid', (async () => { hierarchicalGrid.verticalScrollContainer.scrollTo(2); await wait(100); fixture.detectChanges(); @@ -777,12 +777,6 @@ describe('IgxHierarchicalGrid Complex Navigation', () => { expect(lastCell.rowIndex).toBe(4); })); - - xit('ViewDestroyedError: Attempt to use a destroyed view trigger', (async () => { - // Repro for leftover un-destroyed grid views (leaked resize handler): - // fit this and the test above - window.dispatchEvent(new Event('resize')); - })); }); describe('IgxHierarchicalGrid Multi-layout Navigation', () => { diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts index c4f11514433..129bd35c619 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts @@ -9,6 +9,7 @@ import { wait } from '../../test-utils/ui-interactions.spec'; import { FilteringExpressionsTree, FilteringLogic, IgxStringFilteringOperand } from 'igniteui-angular'; import { By } from '@angular/platform-browser'; import { first, delay } from 'rxjs/operators'; +import { setupHierarchicalGridScrollDetection } from '../../test-utils/helper-utils.spec'; describe('IgxHierarchicalGrid Virtualization', () => { configureTestSuite(); @@ -84,47 +85,45 @@ describe('IgxHierarchicalGrid Virtualization', () => { await wait(3 * 16); }); - xit('Should retain child grid states (scroll position, selection, filtering, paging etc.) when scrolling', fakeAsync(() => { + it('Should retain child grid states (scroll position, selection, filtering, paging etc.) when scrolling', async () => { + setupHierarchicalGridScrollDetection(fixture, hierarchicalGrid); const firstRow = hierarchicalGrid.dataRowList.toArray()[0]; // first child of the row should expand indicator firstRow.nativeElement.children[0].click(); fixture.detectChanges(); const childGrid = hierarchicalGrid.hgridAPI.getChildGrids(false)[0]; - const childCell = childGrid.dataRowList.toArray()[4].cells.toArray()[0]; + const childCell = childGrid.getCellByColumn(0, 'ID'); childCell.nativeElement.focus(); - tick(10); + fixture.detectChanges(); const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { fieldName: 'ProductName', - searchVal: 'Product: A4', + searchVal: 'Product: A0', condition: IgxStringFilteringOperand.instance().condition('startsWith') }; filteringExpressionsTree.filteringOperands.push(expression); childGrid.filter('ProductName', null, filteringExpressionsTree); - tick(); + await wait(); fixture.detectChanges(); expect(childGrid.rowList.length).toEqual(1); - expect(childGrid.rowList.toArray()[0].cells.toArray()[0].selected).toBeTruthy(); + expect(childGrid.getCellByColumn(0, 'ID').selected).toBeTruthy(); const verticalScroll = fixture.componentInstance.hgrid.verticalScrollContainer; const elem = verticalScroll['vh'].instance.elementRef.nativeElement; // scroll down elem.scrollTop = 1000; - fixture.detectChanges(); - fixture.componentRef.hostView.detectChanges(); - tick(); + await wait(); + // scroll to top elem.scrollTop = 0; - fixture.detectChanges(); - fixture.componentRef.hostView.detectChanges(); - tick(); + await wait(); expect(childGrid.rowList.length).toEqual(1); - expect(childGrid.rowList.toArray()[0].cells.toArray()[0].selected).toBeTruthy(); - })); + expect(childGrid.getCellByColumn(0, 'ID').selected).toBeTruthy(); + }); it('should render correct data for child grid after scrolling and start index changes.', async() => { const firstRow = hierarchicalGrid.dataRowList.toArray()[0];