Skip to content

Commit c875851

Browse files
fix(ivy): remove query results from embedded views on view destroy (angular#29056)
PR Close angular#29056
1 parent d2f015f commit c875851

File tree

2 files changed

+22
-19
lines changed

2 files changed

+22
-19
lines changed

packages/core/src/render3/node_manipulation.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ import {NodeInjectorFactory} from './interfaces/injector';
1717
import {TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
1818
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
1919
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
20-
import {CHILD_HEAD, CLEANUP, FLAGS, HEADER_OFFSET, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
20+
import {CHILD_HEAD, CLEANUP, FLAGS, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
2121
import {assertNodeType} from './node_assert';
2222
import {renderStringify} from './util/misc_utils';
2323
import {findComponentView, getLViewParent} from './util/view_traversal_utils';
24-
import {getNativeByTNode, isComponent, isLContainer, isLView, isRootView, unwrapRNode} from './util/view_utils';
24+
import {getNativeByTNode, isComponent, isLContainer, isLView, isRootView, unwrapRNode, viewAttachedToContainer} from './util/view_utils';
2525

2626
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
2727

@@ -350,7 +350,8 @@ export function detachView(lContainer: LContainer, removeIndex: number): LView {
350350
views.splice(removeIndex, 1);
351351
addRemoveViewFromContainer(viewToDetach, false);
352352

353-
if (viewToDetach[QUERIES]) {
353+
if ((viewToDetach[FLAGS] & LViewFlags.Attached) &&
354+
!(viewToDetach[FLAGS] & LViewFlags.Destroyed) && viewToDetach[QUERIES]) {
354355
viewToDetach[QUERIES] !.removeView();
355356
}
356357
viewToDetach[PARENT] = null;
@@ -422,10 +423,8 @@ export function getParentState(lViewOrLContainer: LView | LContainer, rootView:
422423
*
423424
* @param view The LView to clean up
424425
*/
425-
function cleanUpView(viewOrContainer: LView | LContainer): void {
426-
if ((viewOrContainer as LView).length >= HEADER_OFFSET) {
427-
const view = viewOrContainer as LView;
428-
426+
function cleanUpView(view: LView | LContainer): void {
427+
if (isLView(view) && !(view[FLAGS] & LViewFlags.Destroyed)) {
429428
// Usually the Attached flag is removed when the view is detached from its parent, however
430429
// if it's a root view, the flag won't be unset hence why we're also removing on destroy.
431430
view[FLAGS] &= ~LViewFlags.Attached;
@@ -445,6 +444,10 @@ function cleanUpView(viewOrContainer: LView | LContainer): void {
445444
ngDevMode && ngDevMode.rendererDestroy++;
446445
(view[RENDERER] as ProceduralRenderer3).destroy();
447446
}
447+
// For embedded views still attached to a container: remove query result from this view.
448+
if (viewAttachedToContainer(view) && view[QUERIES]) {
449+
view[QUERIES] !.removeView();
450+
}
448451
}
449452
}
450453

packages/core/test/linker/query_integration_spec.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -713,25 +713,25 @@ describe('Query API', () => {
713713
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['2', '1']);
714714
});
715715

716-
fixmeIvy('FW-920: Queries in nested views are not destroyed properly')
717-
.it('should remove manually projected templates if their parent view is destroyed', () => {
718-
const template = `
716+
it('should remove manually projected templates if their parent view is destroyed', () => {
717+
const template = `
719718
<manual-projecting #q><ng-template #tpl><div text="1"></div></ng-template></manual-projecting>
720719
<div *ngIf="shouldShow">
721720
<ng-container [ngTemplateOutlet]="tpl"></ng-container>
722721
</div>
723722
`;
724-
const view = createTestCmp(MyComp0, template);
725-
const q = view.debugElement.children[0].references !['q'];
726-
view.componentInstance.shouldShow = true;
727-
view.detectChanges();
723+
const view = createTestCmp(MyComp0, template);
724+
const q = view.debugElement.children[0].references !['q'];
725+
view.componentInstance.shouldShow = true;
726+
view.detectChanges();
727+
728+
expect(q.query.length).toBe(1);
728729

729-
expect(q.query.length).toBe(1);
730+
view.componentInstance.shouldShow = false;
731+
view.detectChanges();
730732

731-
view.componentInstance.shouldShow = false;
732-
view.detectChanges();
733-
expect(q.query.length).toBe(0);
734-
});
733+
expect(q.query.length).toBe(0);
734+
});
735735

736736
modifiedInIvy('https://github.com/angular/angular/issues/15117 fixed in ivy')
737737
.it('should not throw if a content template is queried and created in the view during change detection',

0 commit comments

Comments
 (0)