From 0299e1609cef3f94eab3797a88cc9fdc9118452c Mon Sep 17 00:00:00 2001
From: Hristo Deshev <hristo@deshev.com>
Date: Wed, 5 Apr 2017 11:46:58 +0300
Subject: [PATCH] fix(list-view): Recycle items for component children

We were not recycling item views if the item contained a single
custom component child.
---
 .../directives/list-view-comp.ts              | 11 ++++++++-
 tests/app/tests/list-view-tests.ts            | 24 +++++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/nativescript-angular/directives/list-view-comp.ts b/nativescript-angular/directives/list-view-comp.ts
index e660f8e0f..ebdf9350a 100644
--- a/nativescript-angular/directives/list-view-comp.ts
+++ b/nativescript-angular/directives/list-view-comp.ts
@@ -25,6 +25,8 @@ import { ListView } from "tns-core-modules/ui/list-view";
 import { View, KeyedTemplate } from "tns-core-modules/ui/core/view";
 import { ObservableArray } from "tns-core-modules/data/observable-array";
 import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base";
+import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
+import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container";
 import { listViewLog } from "../trace";
 
 const NG_VIEW = "_ngViewRef";
@@ -160,6 +162,7 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit {
 
         if (args.view && args.view[NG_VIEW]) {
             listViewLog("onItemLoading: " + index + " - Reusing existing view");
+            console.log("onItemLoading: " + index + " - Reusing existing view");
             viewRef = args.view[NG_VIEW];
             // getting angular view from original element (in cases when ProxyViewContainer
             // is used NativeScript internally wraps it in a StackLayout)
@@ -169,6 +172,7 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit {
             }
         } else {
             listViewLog("onItemLoading: " + index + " - Creating view from template");
+            console.log("onItemLoading: " + index + " - Creating view from template");
             viewRef = this.loader.createEmbeddedView(this.itemTemplate, new ListItemContext(), 0);
             args.view = getItemViewRoot(viewRef);
             args.view[NG_VIEW] = viewRef;
@@ -242,7 +246,12 @@ export interface ComponentView {
 export type RootLocator = (nodes: Array<any>, nestLevel: number) => View;
 
 export function getItemViewRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View {
-    const rootView = rootLocator(viewRef.rootNodes, 0);
+    let rootView = rootLocator(viewRef.rootNodes, -1);
+    if (rootView instanceof ProxyViewContainer) {
+        const wrapperLayout = new StackLayout();
+        wrapperLayout.addChild(rootView);
+        rootView = wrapperLayout;
+    }
     return rootView;
 }
 
diff --git a/tests/app/tests/list-view-tests.ts b/tests/app/tests/list-view-tests.ts
index 6dfae0c74..e8b731b92 100644
--- a/tests/app/tests/list-view-tests.ts
+++ b/tests/app/tests/list-view-tests.ts
@@ -3,6 +3,8 @@ 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 { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
+import { Label } from "tns-core-modules/ui/label";
 
 // import trace = require("trace");
 // trace.setCategories("ns-list-view, " + trace.categories.Navigation);
@@ -121,3 +123,25 @@ describe("ListView-tests", () => {
             .catch(done);
     });
 });
+
+describe("ListView item templates", () => {
+    it("wraps components in StackLayout", () => {
+        const view: ComponentView = {
+            rootNodes: [],
+            destroy: () => {}
+        };
+        const childRoot = new ProxyViewContainer();
+        const itemRoot = getItemViewRoot(view, (_rootNodes, _level) => childRoot);
+        assert.isTrue(itemRoot instanceof StackLayout, "ProxyViewContainer wrapped in StackLayout");
+    });
+
+    it("does not wrap non-component children", () => {
+        const view: ComponentView = {
+            rootNodes: [],
+            destroy: () => {}
+        };
+        const childRoot = new Label();
+        const itemRoot = getItemViewRoot(view, (_rootNodes, _level) => childRoot);
+        assert.isTrue(itemRoot instanceof Label, "'normal' children not wrapped");
+    });
+});