From 71301aacb429e21e2605f5f973e6b8709d0cc858 Mon Sep 17 00:00:00 2001 From: Hristo Deshev Date: Wed, 15 Mar 2017 11:03:23 +0200 Subject: [PATCH] fix(list-view): Destroy item views on unload --- .../directives/list-view-comp.ts | 20 +++++++++---- tests/app/tests/list-view-tests.ts | 29 +++++++++++++++---- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/nativescript-angular/directives/list-view-comp.ts b/nativescript-angular/directives/list-view-comp.ts index 65f2bbd7d..e3642c000 100644 --- a/nativescript-angular/directives/list-view-comp.ts +++ b/nativescript-angular/directives/list-view-comp.ts @@ -137,7 +137,7 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit { listViewLog("registerTemplate for key: " + key); const viewRef = this.loader.createEmbeddedView(template, new ListItemContext(), 0); - const resultView = getSingleViewFromViewRef(viewRef); + const resultView = getItemViewRoot(viewRef); resultView[NG_VIEW] = viewRef; return resultView; @@ -170,7 +170,7 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit { } else { listViewLog("onItemLoading: " + index + " - Creating view from template"); viewRef = this.loader.createEmbeddedView(this.itemTemplate, new ListItemContext(), 0); - args.view = getSingleViewFromViewRef(viewRef); + args.view = getItemViewRoot(viewRef); args.view[NG_VIEW] = viewRef; } @@ -215,7 +215,7 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit { } } -function getSingleViewRecursive(nodes: Array, nestLevel: number) { +function getSingleViewRecursive(nodes: Array, nestLevel: number): View { const actualNodes = nodes.filter((n) => !!n && n.nodeName !== "#text"); if (actualNodes.length === 0) { @@ -235,8 +235,18 @@ function getSingleViewRecursive(nodes: Array, nestLevel: number) { } } -function getSingleViewFromViewRef(viewRef: EmbeddedViewRef): View { - return getSingleViewRecursive(viewRef.rootNodes, 0); +export interface ComponentView { + rootNodes: Array; + destroy(): void; +}; +export type RootLocator = (nodes: Array, nestLevel: number) => View; + +export function getItemViewRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View { + const rootView = rootLocator(viewRef.rootNodes, 0); + rootView.on("unloaded", () => { + viewRef.destroy(); + }); + return rootView; } @Directive({ selector: "[nsTemplateKey]" }) diff --git a/tests/app/tests/list-view-tests.ts b/tests/app/tests/list-view-tests.ts index afc7a6488..cd5dfba39 100644 --- a/tests/app/tests/list-view-tests.ts +++ b/tests/app/tests/list-view-tests.ts @@ -1,6 +1,8 @@ import { assert } from "./test-config"; -import { Component, Input, AfterViewInit } from '@angular/core'; +import { Component, Input, AfterViewInit } from "@angular/core"; import { TestApp } from "./test-app"; +import { RootLocator, ComponentView, getItemViewRoot } from "nativescript-angular/directives/list-view-comp"; +import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container"; // import trace = require("trace"); // trace.setCategories("ns-list-view, " + trace.categories.Navigation); @@ -20,7 +22,7 @@ const ITEMS = [ let testTemplates: { first: number, second: number }; @Component({ - selector: 'list-view-setupItemView', + selector: "list-view-setupItemView", template: ` @@ -61,7 +63,6 @@ export class ItemTemplateComponent { - @@ -77,7 +78,7 @@ export class TestListViewSelectorComponent { constructor() { testTemplates = { first: 0, second: 0 }; } } -describe('ListView-tests', () => { +describe("ListView-tests", () => { let testApp: TestApp = null; before(() => { @@ -94,7 +95,7 @@ describe('ListView-tests', () => { testApp.disposeComponents(); }); - it('setupItemView is called for every item', (done) => { + it("setupItemView is called for every item", (done) => { return testApp.loadComponent(TestListViewComponent).then((componentRef) => { const component = componentRef.instance; setTimeout(() => { @@ -106,7 +107,7 @@ describe('ListView-tests', () => { }); - it('itemTemplateSelector selects templates', (done) => { + it("itemTemplateSelector selects templates", (done) => { return testApp.loadComponent(TestListViewSelectorComponent).then((componentRef) => { setTimeout(() => { assert.deepEqual(testTemplates, { first: 2, second: 1 }); @@ -116,3 +117,19 @@ describe('ListView-tests', () => { .catch(done); }); }); + +describe("ListView item templates", () => { + it("destroy child ng views on unload", () => { + const childRoot = new ProxyViewContainer(); + let viewDestroyed = false; + const view: ComponentView = { + rootNodes: [], + destroy: () => { + viewDestroyed = true; + } + }; + const itemRoot = getItemViewRoot(view, (_rootNodes, _level) => childRoot); + itemRoot.notify({eventName: "unloaded", object: itemRoot}); + assert.isTrue(viewDestroyed, "ng view not destroyed"); + }); +});