diff --git a/nativescript-angular/directives/templated-items-comp.ts b/nativescript-angular/directives/templated-items-comp.ts index f3644f071..a70746796 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() { diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index ea6998cde..450c27f5d 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()`); + } + + while (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; } diff --git a/nativescript-angular/router/ns-location-strategy.ts b/nativescript-angular/router/ns-location-strategy.ts index 6eab73c06..58d4962d2 100644 --- a/nativescript-angular/router/ns-location-strategy.ts +++ b/nativescript-angular/router/ns-location-strategy.ts @@ -744,4 +744,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; + } } diff --git a/nativescript-angular/router/page-router-outlet.ts b/nativescript-angular/router/page-router-outlet.ts index 7c018487b..fb9be5320 100644 --- a/nativescript-angular/router/page-router-outlet.ts +++ b/nativescript-angular/router/page-router-outlet.ts @@ -198,7 +198,16 @@ 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) { + const c = this.activated.instance; + this.activated.hostView.detach(); + destroyComponentRef(this.activated); + + this.deactivateEvents.emit(c); + this.activated = null; } } @@ -349,9 +358,9 @@ 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 - page.content = componentView; + this.viewUtil.insertChild(page, componentView); const navigatedFromCallback = (global).Zone.current.wrap((args: NavigatedData) => { if (args.isBackNavigation) { diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 5a3dea4c8..6a8fbcb5b 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) { @@ -169,8 +170,21 @@ export class ViewUtil { const extendedParent = this.ensureNgViewExtensions(parent); const extendedChild = this.ensureNgViewExtensions(child); + // Remove the child's children and their children + // 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}`); + } + + this.removeChild(extendedChild, grandchild); + } + this.removeFromQueue(extendedParent, extendedChild); - this.removeFromVisualTree(extendedParent, extendedChild); + if (!isDetachedElement(extendedChild)) { + this.removeFromVisualTree(extendedParent, extendedChild); + } } private removeFromQueue(parent: NgView, child: NgView) { @@ -181,6 +195,7 @@ export class ViewUtil { if (parent.firstChild === child && parent.lastChild === child) { parent.firstChild = null; parent.lastChild = null; + child.nextSibling = null; return; } @@ -196,6 +211,8 @@ export class ViewUtil { if (previous) { previous.nextSibling = child.nextSibling; } + + child.nextSibling = null; } // NOTE: This one is O(n) - use carefully @@ -240,7 +257,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) { @@ -249,8 +266,6 @@ 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 (isView(parent)) { parent._removeView(child); } @@ -273,14 +288,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 +316,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);