From c875851bb4bc47aadab9cca3310c279d26399bc5 Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Fri, 1 Mar 2019 14:45:04 +0100 Subject: [PATCH] fix(ivy): remove query results from embedded views on view destroy (#29056) PR Close #29056 --- .../core/src/render3/node_manipulation.ts | 17 +++++++------ .../test/linker/query_integration_spec.ts | 24 +++++++++---------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 9a73023438..59322330d5 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -17,11 +17,11 @@ import {NodeInjectorFactory} from './interfaces/injector'; import {TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; -import {CHILD_HEAD, CLEANUP, FLAGS, HEADER_OFFSET, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; +import {CHILD_HEAD, CLEANUP, FLAGS, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {assertNodeType} from './node_assert'; import {renderStringify} from './util/misc_utils'; import {findComponentView, getLViewParent} from './util/view_traversal_utils'; -import {getNativeByTNode, isComponent, isLContainer, isLView, isRootView, unwrapRNode} from './util/view_utils'; +import {getNativeByTNode, isComponent, isLContainer, isLView, isRootView, unwrapRNode, viewAttachedToContainer} from './util/view_utils'; const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5; @@ -350,7 +350,8 @@ export function detachView(lContainer: LContainer, removeIndex: number): LView { views.splice(removeIndex, 1); addRemoveViewFromContainer(viewToDetach, false); - if (viewToDetach[QUERIES]) { + if ((viewToDetach[FLAGS] & LViewFlags.Attached) && + !(viewToDetach[FLAGS] & LViewFlags.Destroyed) && viewToDetach[QUERIES]) { viewToDetach[QUERIES] !.removeView(); } viewToDetach[PARENT] = null; @@ -422,10 +423,8 @@ export function getParentState(lViewOrLContainer: LView | LContainer, rootView: * * @param view The LView to clean up */ -function cleanUpView(viewOrContainer: LView | LContainer): void { - if ((viewOrContainer as LView).length >= HEADER_OFFSET) { - const view = viewOrContainer as LView; - +function cleanUpView(view: LView | LContainer): void { + if (isLView(view) && !(view[FLAGS] & LViewFlags.Destroyed)) { // Usually the Attached flag is removed when the view is detached from its parent, however // if it's a root view, the flag won't be unset hence why we're also removing on destroy. view[FLAGS] &= ~LViewFlags.Attached; @@ -445,6 +444,10 @@ function cleanUpView(viewOrContainer: LView | LContainer): void { ngDevMode && ngDevMode.rendererDestroy++; (view[RENDERER] as ProceduralRenderer3).destroy(); } + // For embedded views still attached to a container: remove query result from this view. + if (viewAttachedToContainer(view) && view[QUERIES]) { + view[QUERIES] !.removeView(); + } } } diff --git a/packages/core/test/linker/query_integration_spec.ts b/packages/core/test/linker/query_integration_spec.ts index 1fd7bbc11c..bd6bd37109 100644 --- a/packages/core/test/linker/query_integration_spec.ts +++ b/packages/core/test/linker/query_integration_spec.ts @@ -713,25 +713,25 @@ describe('Query API', () => { expect(q.query.map((d: TextDirective) => d.text)).toEqual(['2', '1']); }); - fixmeIvy('FW-920: Queries in nested views are not destroyed properly') - .it('should remove manually projected templates if their parent view is destroyed', () => { - const template = ` + it('should remove manually projected templates if their parent view is destroyed', () => { + const template = `
`; - const view = createTestCmp(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; - view.componentInstance.shouldShow = true; - view.detectChanges(); + const view = createTestCmp(MyComp0, template); + const q = view.debugElement.children[0].references !['q']; + view.componentInstance.shouldShow = true; + view.detectChanges(); - expect(q.query.length).toBe(1); + expect(q.query.length).toBe(1); - view.componentInstance.shouldShow = false; - view.detectChanges(); - expect(q.query.length).toBe(0); - }); + view.componentInstance.shouldShow = false; + view.detectChanges(); + + expect(q.query.length).toBe(0); + }); modifiedInIvy('https://github.com/angular/angular/issues/15117 fixed in ivy') .it('should not throw if a content template is queried and created in the view during change detection',