Skip to content

Commit cb56b48

Browse files
committed
fix(list-view): Destroy item views on unload
1 parent 0bd2ba5 commit cb56b48

File tree

2 files changed

+38
-11
lines changed

2 files changed

+38
-11
lines changed

nativescript-angular/directives/list-view-comp.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit {
137137
listViewLog("registerTemplate for key: " + key);
138138

139139
const viewRef = this.loader.createEmbeddedView(template, new ListItemContext(), 0);
140-
const resultView = getSingleViewFromViewRef(viewRef);
140+
const resultView = getItemViewRoot(viewRef);
141141
resultView[NG_VIEW] = viewRef;
142142

143143
return resultView;
@@ -170,7 +170,7 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit {
170170
} else {
171171
listViewLog("onItemLoading: " + index + " - Creating view from template");
172172
viewRef = this.loader.createEmbeddedView(this.itemTemplate, new ListItemContext(), 0);
173-
args.view = getSingleViewFromViewRef(viewRef);
173+
args.view = getItemViewRoot(viewRef);
174174
args.view[NG_VIEW] = viewRef;
175175
}
176176

@@ -215,7 +215,7 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit {
215215
}
216216
}
217217

218-
function getSingleViewRecursive(nodes: Array<any>, nestLevel: number) {
218+
function getSingleViewRecursive(nodes: Array<any>, nestLevel: number): View {
219219
const actualNodes = nodes.filter((n) => !!n && n.nodeName !== "#text");
220220

221221
if (actualNodes.length === 0) {
@@ -235,8 +235,18 @@ function getSingleViewRecursive(nodes: Array<any>, nestLevel: number) {
235235
}
236236
}
237237

238-
function getSingleViewFromViewRef(viewRef: EmbeddedViewRef<any>): View {
239-
return getSingleViewRecursive(viewRef.rootNodes, 0);
238+
export interface ComponentView {
239+
rootNodes: Array<any>
240+
destroy(): void;
241+
};
242+
export type RootLocator = (nodes: Array<any>, nestLevel: number) => View;
243+
244+
export function getItemViewRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View {
245+
const rootView = rootLocator(viewRef.rootNodes, 0);
246+
rootView.on("unloaded", () => {
247+
viewRef.destroy();
248+
});
249+
return rootView;
240250
}
241251

242252
@Directive({ selector: "[nsTemplateKey]" })

tests/app/tests/list-view-tests.ts

+23-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { assert } from "./test-config";
2-
import { Component, Input, AfterViewInit } from '@angular/core';
2+
import { Component, Input, AfterViewInit } from "@angular/core";
33
import { TestApp } from "./test-app";
4+
import { RootLocator, ComponentView, getItemViewRoot } from "nativescript-angular/directives/list-view-comp";
5+
import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container";
46

57
// import trace = require("trace");
68
// trace.setCategories("ns-list-view, " + trace.categories.Navigation);
@@ -20,7 +22,7 @@ const ITEMS = [
2022
let testTemplates: { first: number, second: number };
2123

2224
@Component({
23-
selector: 'list-view-setupItemView',
25+
selector: "list-view-setupItemView",
2426
template: `
2527
<GridLayout>
2628
<ListView [items]="myItems" (setupItemView)="onSetupItemView($event)">
@@ -61,7 +63,6 @@ export class ItemTemplateComponent {
6163
<template nsTemplateKey="first">
6264
<item-component templateName="first"></item-component>
6365
</template>
64-
6566
<template nsTemplateKey="second" let-item="item">
6667
<item-component templateName="second"></item-component>
6768
</template>
@@ -77,7 +78,7 @@ export class TestListViewSelectorComponent {
7778
constructor() { testTemplates = { first: 0, second: 0 }; }
7879
}
7980

80-
describe('ListView-tests', () => {
81+
describe("ListView-tests", () => {
8182
let testApp: TestApp = null;
8283

8384
before(() => {
@@ -94,7 +95,7 @@ describe('ListView-tests', () => {
9495
testApp.disposeComponents();
9596
});
9697

97-
it('setupItemView is called for every item', (done) => {
98+
it("setupItemView is called for every item", (done) => {
9899
return testApp.loadComponent(TestListViewComponent).then((componentRef) => {
99100
const component = componentRef.instance;
100101
setTimeout(() => {
@@ -106,7 +107,7 @@ describe('ListView-tests', () => {
106107
});
107108

108109

109-
it('itemTemplateSelector selects templates', (done) => {
110+
it("itemTemplateSelector selects templates", (done) => {
110111
return testApp.loadComponent(TestListViewSelectorComponent).then((componentRef) => {
111112
setTimeout(() => {
112113
assert.deepEqual(testTemplates, { first: 2, second: 1 });
@@ -116,3 +117,19 @@ describe('ListView-tests', () => {
116117
.catch(done);
117118
});
118119
});
120+
121+
describe("ListView item templates", () => {
122+
it("destroy child ng views on unload", () => {
123+
const childRoot = new ProxyViewContainer();
124+
let viewDestroyed = false;
125+
const view: ComponentView = {
126+
rootNodes: [],
127+
destroy: () => {
128+
viewDestroyed = true;
129+
}
130+
};
131+
const itemRoot = getItemViewRoot(view, (_rootNodes, _level) => childRoot);
132+
itemRoot.notify({eventName: "unloaded", object: itemRoot});
133+
assert.isTrue(viewDestroyed, "ng view not destroyed");
134+
});
135+
});

0 commit comments

Comments
 (0)