From 652da97bddfef8978b2f6e966e2fb86465b908b0 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 3 Mar 2020 16:37:46 +1300 Subject: [PATCH 1/3] test(test-app-ng) Add test listView recreating the issue The issue occurs when a list is modified and the segmented bar is reloaded upon scrolling with existing SegmentedBarItems. --- e2e/tests-app-ng/app/app.routes.ts | 6 + .../list-view-main-page.component.ts | 1 + ...iew-nested-segmented-bar-page.component.ts | 127 ++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts diff --git a/e2e/tests-app-ng/app/app.routes.ts b/e2e/tests-app-ng/app/app.routes.ts index d68882440..486328ba2 100644 --- a/e2e/tests-app-ng/app/app.routes.ts +++ b/e2e/tests-app-ng/app/app.routes.ts @@ -17,6 +17,7 @@ import { ListViewComponent } from "./list-view/list-view-page.component"; import { ListViewControlComponent } from "./list-view/list-view-item-template.component"; import { ListViewAsyncPipeComponent } from "./list-view/async-pipe-template.component"; import { ListViewMainPageComponent } from "./list-view/list-view-main-page.component"; +import { ListViewSegmentedBarPageComponent } from "./list-view/list-view-nested-segmented-bar-page.component"; import { ListViewWithNestedTemplateComponent } from "./list-view/list-view-nested-template.component"; import { ListViewMultipleTemplatesComponent } from "./list-view/multiple-templates.component"; @@ -68,6 +69,7 @@ export const routableComponents = [ ListViewComponent, ListViewControlComponent, ListViewAsyncPipeComponent, + ListViewSegmentedBarPageComponent, ListViewWithNestedTemplateComponent, ListViewMultipleTemplatesComponent, @@ -131,6 +133,10 @@ export const routes = [ { path: "ListViewExamples/commonTemplate", component: ListViewComponent, data: { title: "commonTemplate" } }, { path: "ListViewExamples/customTemplate", component: ListViewControlComponent, data: { title: "customTemplate" } }, { path: "listView/asyncPipeTemplate", component: ListViewAsyncPipeComponent, data: { title: "asyncPipeTemplate" } }, + { + path: "ListViewExamples/segmentedBarTemplate", + component: ListViewSegmentedBarPageComponent, + data: { title: "segmentedBarTemplate" } }, { path: "listView/nestedTemplate", component: ListViewWithNestedTemplateComponent, diff --git a/e2e/tests-app-ng/app/list-view/list-view-main-page.component.ts b/e2e/tests-app-ng/app/list-view/list-view-main-page.component.ts index d08c565ba..3b7e95756 100644 --- a/e2e/tests-app-ng/app/list-view/list-view-main-page.component.ts +++ b/e2e/tests-app-ng/app/list-view/list-view-main-page.component.ts @@ -7,6 +7,7 @@ import { Component } from "@angular/core"; + diff --git a/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts b/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts new file mode 100644 index 000000000..1173c764d --- /dev/null +++ b/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts @@ -0,0 +1,127 @@ +import { Component, ViewChild, ElementRef, OnInit } from "@angular/core"; +import { SegmentedBarItem, SegmentedBar } from "tns-core-modules/ui/segmented-bar/segmented-bar"; +import { ListView } from "tns-core-modules/ui/list-view/list-view"; +import { EventData } from "tns-core-modules/ui/page/page"; + +interface DataItem { + id: number; + name: string; + type: string; +} + +@Component({ + moduleId: module.id, + selector: "segmented-bar-list-test", + template: ` + + + + + + + + + + + + + + + + + + + + + + + `, +}) +export class ListViewSegmentedBarPageComponent implements OnInit { + public displayedItems: Array = []; + public items: Array; + public segmentedBarItems: SegmentedBarItem[] = this.createSegmentedBarItems(); + + @ViewChild("listViewTest", { static: false }) + private listViewTest?: ElementRef; + + constructor() { + this.items = []; + + for (let i = 0; i < 20; i++) { + const type = "dataItemTemplate"; + + this.items.push({ + id: i, + name: `data item ${i}`, + type: type, + }); + } + } + + public ngOnInit() { + this.displayedItems = this.updateItems(true); + } + + public onButtonPress() { + // tslint:disable-next-line: no-unused-expression + new Promise((resolve) => { + setTimeout(() => { + if (this.listViewTest) { + console.log("Scrolling to the top of the list..."); + const listView = this.listViewTest.nativeElement as ListView; + listView.scrollToIndex(0); + } + resolve(); + }, 150); + }); + + this.displayedItems = this.updateItems(false); + } + + public onSegmentedBarPress(args: EventData) { + if (args && args.object) { + const segmentBar = args.object as SegmentedBar; + const selectedOdd = segmentBar.selectedIndex === 0; + this.displayedItems = this.updateItems(selectedOdd); + } + } + + public createSegmentedBarItems() { + const itemOdd = new SegmentedBarItem(); + itemOdd.title = "Odd Items"; + const itemEven = new SegmentedBarItem(); + itemEven.title = "Even Items"; + return [itemOdd, itemEven]; + } + + public templateSelector(item: DataItem): string { + return item.type; + } + + private updateItems(odd: boolean) { + const items = [ + { + id: -1, + name: "Segmented Bar", + type: "segmentedBarTemplate", + }, + ...(odd + ? this.items.filter((item) => item.id % 2 === 1) + : this.items.filter((item) => item.id % 2 === 0)), + { + id: 999, + name: "Refresh test", + type: "buttonTemplate", + }, + ]; + return items; + } +} From 2e5822ee04b638f378ba866db5aba1e829f51e14 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 3 Mar 2020 16:39:23 +1300 Subject: [PATCH 2/3] fix(view-util) Check if a value is currently assigned to another parent. If the value is assigned, dereference the current parent assignment. --- nativescript-angular/view-util.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 97a2f6ba3..7b3ad0a8e 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -260,6 +260,10 @@ export class ViewUtil { } private removeLayoutChild(parent: NgLayoutBase, child: NgView): void { + if (isLogEnabled()) { + traceLog(`ViewUtil.removeLayoutChild parent: ${parent} child: ${child}`); + } + const index = parent.getChildIndex(child); if (index !== -1) { @@ -371,6 +375,12 @@ export class ViewUtil { const propMap = this.getProperties(view); const propertyName = propMap.get(attributeName); + + // Ensure the children of a collection currently have no parent set. + if (Array.isArray(value)) { + this.removeParentReferencesFromItems(value); + } + if (propertyName) { // We have a lower-upper case mapped property. view[propertyName] = value; @@ -381,6 +391,18 @@ export class ViewUtil { view[attributeName] = value; } + private removeParentReferencesFromItems(items: any[]): void { + for (const item of items) { + if (item.parent && item.parentNode) { + if (isLogEnabled()) { + traceLog(`Unassigning parent ${item.parentNode} on value: ${item}`); + } + item.parent = undefined; + item.parentNode = undefined; + } + } + } + private getProperties(instance: any): Map { const type = instance && instance.constructor; if (!type) { From 45d94d267331122a5684fd71f409308dad969a57 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 3 Mar 2020 17:27:33 +1300 Subject: [PATCH 3/3] test(test-app-ng) clean up text in list view --- .../list-view-nested-segmented-bar-page.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts b/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts index 1173c764d..ec0d9d963 100644 --- a/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts +++ b/e2e/tests-app-ng/app/list-view/list-view-nested-segmented-bar-page.component.ts @@ -32,8 +32,8 @@ interface DataItem { - - + +