Skip to content

Fix/ns views not cleaned on removal #1740

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
May 10, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8f70bd0
fix(TemplatedItemsComponent): remove templates in ngOnDestroy()
m-abs Feb 18, 2019
e3d494f
fix(PageRouterOutlet): Destroy the activated componentRef on ngOnDest…
m-abs Feb 18, 2019
fa1d69b
fix(NativeScriptRendererFactory): Empty the rootNgView when NativeScr…
m-abs Feb 18, 2019
8c81711
fix(ViewUtil): ViewUtil.removeChild() doesn't remove the child recurs…
m-abs Feb 18, 2019
9d479a9
fix(NSLocationStrategy): remove outlets in ngOnDestroy()
m-abs Feb 18, 2019
cf4eba4
fix: PageRouterOutlet.ngOnDestroy logged as NSLocationStrategy.ngOnDe…
m-abs Feb 18, 2019
493d062
fix(ViewUtil): Removing FormattedString throw
m-abs Feb 19, 2019
616c4c2
fix: use ViewUtils to add Component to Page
m-abs Feb 20, 2019
3bf1b8a
fix: Remove all the children from the AppHostView
m-abs Feb 20, 2019
27baf95
Merge branch 'master' into fix/ns-views-not-cleaned-on-removal
m-abs Feb 20, 2019
e74afc2
fix: ViewUtil.removeFromVisualTree logged under wrong name
m-abs Feb 20, 2019
b9b55b6
fix(PageRouterOutlet): Don't remove grandchild views when moving comp…
m-abs Feb 20, 2019
596daec
Merge branch 'master' into fix/ns-views-not-cleaned-on-removal
m-abs Mar 6, 2019
5b4f8ff
fix: ViewUtils tried to remote Span elements from Labels
m-abs Mar 7, 2019
17ecfb5
Merge branch 'master' into fix/ns-views-not-cleaned-on-removal
m-abs Mar 22, 2019
1fb3a63
Merge branch 'master' into fix/ns-views-not-cleaned-on-removal
m-abs Apr 5, 2019
b049e6c
Merge branch 'master' into fix/ns-views-not-cleaned-on-removal
m-abs Apr 15, 2019
9fb6bbb
Merge branch 'master' into fix/ns-views-not-cleaned-on-removal
elena-p Apr 19, 2019
2a64e87
Merge branch 'master' into fix/ns-views-not-cleaned-on-removal
m-abs Apr 24, 2019
4e32e89
Merge branch 'master' into fix/ns-views-not-cleaned-on-removal
m-abs May 8, 2019
ee086c0
chor: skip detached elements for removeFromVisualTree
m-abs May 9, 2019
b9f2568
Merge branch 'master' into fix/ns-views-not-cleaned-on-removal
SvetoslavTsenov May 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions nativescript-angular/directives/templated-items-comp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
12 changes: 11 additions & 1 deletion nativescript-angular/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
Expand Down
9 changes: 9 additions & 0 deletions nativescript-angular/router/ns-location-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
15 changes: 12 additions & 3 deletions nativescript-angular/router/page-router-outlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably no need to call detach on component that will be destroyed anyway

destroyComponentRef(this.activated);

this.deactivateEvents.emit(c);
this.activated = null;
}
}

Expand Down Expand Up @@ -348,9 +357,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 = (<any>global).Zone.current.wrap((args: NavigatedData) => {
if (args.isBackNavigation) {
Expand Down
41 changes: 32 additions & 9 deletions nativescript-angular/view-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -169,6 +170,17 @@ 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);
}
Expand All @@ -181,6 +193,7 @@ export class ViewUtil {
if (parent.firstChild === child && parent.lastChild === child) {
parent.firstChild = null;
parent.lastChild = null;
child.nextSibling = null;
return;
}

Expand All @@ -196,6 +209,8 @@ export class ViewUtil {
if (previous) {
previous.nextSibling = child.nextSibling;
}

child.nextSibling = null;
}

// NOTE: This one is O(n) - use carefully
Expand Down Expand Up @@ -240,7 +255,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) {
Expand All @@ -249,8 +264,11 @@ 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 (child.nodeName === "FormattedString" || child.nodeName === "Span") {
// Removing FormattedString and its Spans from a Label throws an exception
} else if (isView(parent)) {
parent._removeView(child);
}
Expand All @@ -273,14 +291,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 = <NgView>new viewClass();
const ngView = this.setNgViewExtensions(view, name);
Expand All @@ -300,6 +319,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);
Expand Down