From 8f70bd073965723153f9f43afdc89d0080cec5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Mon, 18 Feb 2019 17:00:21 +0100 Subject: [PATCH 01/13] fix(TemplatedItemsComponent): remove templates in ngOnDestroy() --- nativescript-angular/directives/templated-items-comp.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nativescript-angular/directives/templated-items-comp.ts b/nativescript-angular/directives/templated-items-comp.ts index 43e873d2a..b358cac65 100644 --- a/nativescript-angular/directives/templated-items-comp.ts +++ b/nativescript-angular/directives/templated-items-comp.ts @@ -100,6 +100,11 @@ export abstract class TemplatedItemsComponent implements DoCheck, OnDestroy, Aft ngOnDestroy() { this.templatedItemsView.off("itemLoading", this.onItemLoading, this); + this.templatedItemsView = null; + + if (this._templateMap) { + this._templateMap.clear(); + } } private setItemTemplates() { From e3d494fd09255bac26e241c6bc62453cf5ab37d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Mon, 18 Feb 2019 17:02:53 +0100 Subject: [PATCH 02/13] fix(PageRouterOutlet): Destroy the activated componentRef on ngOnDestroy() --- nativescript-angular/router/page-router-outlet.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index 381ff52bd..42f4365fa 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -200,6 +200,15 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire } else { log("NSLocationStrategy.ngOnDestroy: no outlet available for page-router-outlet"); } + + if (this.isActivated) { + const c = this.activated.instance; + this.activated.hostView.detach(); + destroyComponentRef(this.activated); + + this.deactivateEvents.emit(c); + this.activated = null; + } } deactivate(): void { From fa1d69b8a3c9f5539fef46f123dc826c99cc8310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Mon, 18 Feb 2019 17:03:47 +0100 Subject: [PATCH 03/13] fix(NativeScriptRendererFactory): Empty the rootNgView when NativeScriptRendererFactory is destroyed --- nativescript-angular/renderer.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index ea6998cde..376ead139 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -73,6 +73,16 @@ export class NativeScriptRendererFactory implements RendererFactory2 { this.componentRenderers.set(type.id, renderer); return renderer; } + + ngOnDestroy(): void { + if (isLogEnabled()) { + traceLog(`NativeScriptRendererFactory.ngOnDestroy()`); + } + + if (this.rootNgView && this.rootNgView.firstChild) { + this.viewUtil.removeChild(this.rootNgView, this.rootNgView.firstChild); + } + } } export class NativeScriptRenderer extends Renderer2 { @@ -129,7 +139,7 @@ export class NativeScriptRenderer extends Renderer2 { @profile parentNode(node: NgView): any { if (isLogEnabled()) { - traceLog(`NativeScriptRenderer.parentNode for node: ${node}`); + traceLog(`NativeScriptRenderer.parentNode for node: ${node} is ${node.parentNode}`); } return node.parentNode; } From 8c8171102dc0a9f2f847a516b50ed2c8d4150e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Mon, 18 Feb 2019 17:07:02 +0100 Subject: [PATCH 04/13] fix(ViewUtil): ViewUtil.removeChild() doesn't remove the child recursively --- nativescript-angular/view-util.ts | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 5a3dea4c8..4a8a3d112 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -169,6 +169,16 @@ export class ViewUtil { const extendedParent = this.ensureNgViewExtensions(parent); const extendedChild = this.ensureNgViewExtensions(child); + // Remove the child's children and their children + while (extendedChild && extendedChild.firstChild) { + const grandchild = extendedChild.firstChild; + if (isLogEnabled()) { + traceLog(`ViewUtil.removeChild parent: ${parent} child: ${extendedChild} grandchild: ${grandchild}`); + } + + this.removeChild(extendedChild, grandchild); + } + this.removeFromQueue(extendedParent, extendedChild); this.removeFromVisualTree(extendedParent, extendedChild); } @@ -181,6 +191,7 @@ export class ViewUtil { if (parent.firstChild === child && parent.lastChild === child) { parent.firstChild = null; parent.lastChild = null; + child.nextSibling = null; return; } @@ -196,6 +207,8 @@ export class ViewUtil { if (previous) { previous.nextSibling = child.nextSibling; } + + child.nextSibling = null; } // NOTE: This one is O(n) - use carefully @@ -249,8 +262,9 @@ export class ViewUtil { this.removeLayoutChild(parent, child); } else if (isContentView(parent) && parent.content === child) { parent.content = null; - parent.lastChild = null; - parent.firstChild = null; + } else if (child.nodeName === "DetachedContainer") { + // Skip - DetachedContainer is... well detached from its parent + // Used with ListViews and other TemplatedItemsComponent views. } else if (isView(parent)) { parent._removeView(child); } @@ -273,14 +287,15 @@ export class ViewUtil { } public createView(name: string): NgView { - if (isLogEnabled()) { - traceLog(`Creating view: ${name}`); - } - + const originalName = name; if (!isKnownView(name)) { name = "ProxyViewContainer"; } + if (isLogEnabled()) { + traceLog(`Creating view: ${originalName} ${name}`); + } + const viewClass = getViewClass(name); const view = new viewClass(); const ngView = this.setNgViewExtensions(view, name); @@ -300,6 +315,10 @@ export class ViewUtil { } private setNgViewExtensions(view: View, name: string): NgView { + if (isLogEnabled()) { + traceLog(`Make into a NgView view: ${view} name: "${name}"`); + } + const ngView = view as NgView; ngView.nodeName = name; ngView.meta = getViewMeta(name); From 9d479a951ce6f904bcaabed0a51b15609634ff48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Mon, 18 Feb 2019 17:14:33 +0100 Subject: [PATCH 05/13] fix(NSLocationStrategy): remove outlets in ngOnDestroy() Make sure outlets and currentOutlet are removed when the NSLocationStrategy is destroyed. --- nativescript-angular/router/ns-location-strategy.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/nativescript-angular/router/ns-location-strategy.ts b/nativescript-angular/router/ns-location-strategy.ts index 7ac6331ba..8202ec7de 100644 --- a/nativescript-angular/router/ns-location-strategy.ts +++ b/nativescript-angular/router/ns-location-strategy.ts @@ -729,4 +729,13 @@ export class NSLocationStrategy extends LocationStrategy { private getOutletKey(path: string, outletName: string): string { return path ? path + "-" + outletName : outletName; } + + ngOnDestroy() { + if (isLogEnabled()) { + routerLog("NSLocationStrategy.ngOnDestroy()"); + } + + this.outlets = []; + this.currentOutlet = null; + } } From cf4eba454faa99fdc5828f1d0ffc23c1837dbd49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Mon, 18 Feb 2019 17:28:33 +0100 Subject: [PATCH 06/13] fix: PageRouterOutlet.ngOnDestroy logged as NSLocationStrategy.ngOnDestroy --- nativescript-angular/router/page-router-outlet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index 42f4365fa..a9dfeb650 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -198,7 +198,7 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire }); this.locationStrategy.clearOutlet(this.frame); } else { - log("NSLocationStrategy.ngOnDestroy: no outlet available for page-router-outlet"); + log("PageRouterOutlet.ngOnDestroy: no outlet available for page-router-outlet"); } if (this.isActivated) { From 493d0626b04ccd1c4d01e99000e753e1f6e2893f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Tue, 19 Feb 2019 15:41:32 +0100 Subject: [PATCH 07/13] fix(ViewUtil): Removing FormattedString throw When removing the children from a Label and one of these children is a FormattedString, the FormattedTextProperty throw this exception: "TypeError: Cannot read property 'textTransform' of undefined" --- nativescript-angular/view-util.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 4a8a3d112..d81509107 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -265,6 +265,8 @@ export class ViewUtil { } else if (child.nodeName === "DetachedContainer") { // Skip - DetachedContainer is... well detached from its parent // Used with ListViews and other TemplatedItemsComponent views. + } else if (child.nodeName === "FormattedString") { + // Removing FormattedString and its Spans from a Label throws an exception } else if (isView(parent)) { parent._removeView(child); } From 616c4c22fd92a25f21bd966472469fe796d416e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Sj=C3=B8gren?= Date: Wed, 20 Feb 2019 01:29:56 +0100 Subject: [PATCH 08/13] fix: use ViewUtils to add Component to Page Without this the Page and componentView doesn't get the proper relation for then the page is destroyed. --- nativescript-angular/router/page-router-outlet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index a9dfeb650..93cf5bf52 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -359,7 +359,7 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire // Remove it from original native parent. this.viewUtil.removeChild(componentView.parent, componentView); // Add it to the new page - page.content = componentView; + this.viewUtil.insertChild(page, componentView); page.on(Page.navigatedFromEvent, (global).Zone.current.wrap((args: NavigatedData) => { if (args.isBackNavigation) { From 3bf1b8a2110af71dff2a9071fc0cb1e1ca6f3ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Sj=C3=B8gren?= Date: Wed, 20 Feb 2019 01:31:56 +0100 Subject: [PATCH 09/13] fix: Remove all the children from the AppHostView --- nativescript-angular/renderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index 376ead139..450c27f5d 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -79,7 +79,7 @@ export class NativeScriptRendererFactory implements RendererFactory2 { traceLog(`NativeScriptRendererFactory.ngOnDestroy()`); } - if (this.rootNgView && this.rootNgView.firstChild) { + while (this.rootNgView && this.rootNgView.firstChild) { this.viewUtil.removeChild(this.rootNgView, this.rootNgView.firstChild); } } From e74afc24d1c8aa924b9b276c2327435e49309cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Wed, 20 Feb 2019 15:44:57 +0100 Subject: [PATCH 10/13] fix: ViewUtil.removeFromVisualTree logged under wrong name --- nativescript-angular/view-util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index d81509107..8b3647cc7 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -253,7 +253,7 @@ export class ViewUtil { private removeFromVisualTree(parent: NgView, child: NgView) { if (isLogEnabled()) { - traceLog(`ViewUtil.findPreviousElement parent: ${parent} child: ${child}`); + traceLog(`ViewUtil.removeFromVisualTree parent: ${parent} child: ${child}`); } if (parent.meta && parent.meta.removeChild) { From b9b55b6eaecde075cc5b942fad7e11f8f1475b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Wed, 20 Feb 2019 16:13:45 +0100 Subject: [PATCH 11/13] fix(PageRouterOutlet): Don't remove grandchild views when moving component PageRouterOutlet.loadComponentInPage moves the componentView from its native parent, to the Page. Don't remove the componentView's children when it is removed from the native parent. --- nativescript-angular/router/page-router-outlet.ts | 2 +- nativescript-angular/view-util.ts | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index 93cf5bf52..6934dfc25 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -357,7 +357,7 @@ export class PageRouterOutlet implements OnDestroy { // tslint:disable-line:dire // Component loaded. Find its root native view. const componentView = componentRef.location.nativeElement; // Remove it from original native parent. - this.viewUtil.removeChild(componentView.parent, componentView); + this.viewUtil.removeChild(componentView.parent, componentView, false); // Add it to the new page this.viewUtil.insertChild(page, componentView); diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 8b3647cc7..3c2c52c40 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -157,9 +157,10 @@ export class ViewUtil { return next; } - public removeChild(parent: View, child: View) { + public removeChild(parent: View, child: View, removeGrandchildren = true) { if (isLogEnabled()) { - traceLog(`ViewUtil.removeChild parent: ${parent} child: ${child}`); + traceLog(`ViewUtil.removeChild parent: ${parent} child: ${child} ` + + `remove grandchildren: ${removeGrandchildren}`); } if (!parent) { @@ -170,7 +171,8 @@ export class ViewUtil { const extendedChild = this.ensureNgViewExtensions(child); // Remove the child's children and their children - while (extendedChild && extendedChild.firstChild) { + // Unless called from PageRouterOutlet when the child is moved from once parent to another. + while (extendedChild && extendedChild.firstChild && removeGrandchildren) { const grandchild = extendedChild.firstChild; if (isLogEnabled()) { traceLog(`ViewUtil.removeChild parent: ${parent} child: ${extendedChild} grandchild: ${grandchild}`); From 5b4f8ffdf25c2d9f9e62b5f0911c76bfe67faeaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Sj=C3=B8gren?= Date: Thu, 7 Mar 2019 13:53:05 +0100 Subject: [PATCH 12/13] fix: ViewUtils tried to remote Span elements from Labels Just like with FormattedString this throws an exception --- nativescript-angular/view-util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 3c2c52c40..0f9d009e9 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -267,7 +267,7 @@ export class ViewUtil { } else if (child.nodeName === "DetachedContainer") { // Skip - DetachedContainer is... well detached from its parent // Used with ListViews and other TemplatedItemsComponent views. - } else if (child.nodeName === "FormattedString") { + } else if (child.nodeName === "FormattedString" || child.nodeName === "Span") { // Removing FormattedString and its Spans from a Label throws an exception } else if (isView(parent)) { parent._removeView(child); From ee086c0b70e241c8c110c04b1489bc9773fdda7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Anton=20Bach=20Sj=C3=B8gren?= Date: Thu, 9 May 2019 10:24:01 +0200 Subject: [PATCH 13/13] chor: skip detached elements for removeFromVisualTree --- nativescript-angular/view-util.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 0f9d009e9..6a8fbcb5b 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -182,7 +182,9 @@ export class ViewUtil { } this.removeFromQueue(extendedParent, extendedChild); - this.removeFromVisualTree(extendedParent, extendedChild); + if (!isDetachedElement(extendedChild)) { + this.removeFromVisualTree(extendedParent, extendedChild); + } } private removeFromQueue(parent: NgView, child: NgView) { @@ -264,11 +266,6 @@ export class ViewUtil { this.removeLayoutChild(parent, child); } else if (isContentView(parent) && parent.content === child) { parent.content = null; - } else if (child.nodeName === "DetachedContainer") { - // Skip - DetachedContainer is... well detached from its parent - // Used with ListViews and other TemplatedItemsComponent views. - } else if (child.nodeName === "FormattedString" || child.nodeName === "Span") { - // Removing FormattedString and its Spans from a Label throws an exception } else if (isView(parent)) { parent._removeView(child); }