question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Component lifecycles differs on nativescript-angular and angular2 on web.

See original GitHub issue

I ran into this problem and I’ll try to explain it here.

With nativescript-angular the lifecycle hook ngOnDestroy isn’t called when we navigate away from a page as it would be on web. Navigating back reuses the old component with nativescript-angular but a new instance is created on web.

I’ve based an example on Nathan Walker’s angular2-seed-advanced: https://github.com/m-abs/angular2-seed-advanced/tree/ngondestroy-not-triggered

I’ve two component for my routes: HomeComponent AboutComponent

Each component have a instanceNo, so we can identify multiple instances of the component.

Action Web TNS
Launch application HomeComponent<1>.ngOnInit() is called HomeComponent<1>.ngOnInit() is called
Click on ‘ABOUT’ HomeComponent<1>.ngOnDestroy() followed by AboutComponent<1>.ngOnInit() AboutComponent<1>.ngOnInit()
Click on ‘HOME’ AboutComponent<1>.ngOnDestroy() followed by HomeComponent<2>.ngOnInit() HomeComponent<2>.ngOnInit()
Click back HomeComponent<2>.ngOnDestroy() followed by AboutComponent<2>.ngOnInit() HomeComponent<2>.ngOnDestroy()
Click back again AboutComponent<2>.ngOnDestroy() followed by HomeComponent<3>.ngOnInit() AboutComponent<1>.ngOnDestroy()

As you can see the two behaves very differently.

On web:

  • Navigate to a new page, the current component is destroyed and a new is created.
  • Navigating hitting back, the current component is destroyed and a new is created.

With nativescript-angular:

  • Navigate to a new page, creates the new component but leaves the current component as is.
  • Navigating hitting back, the current component is destroyed and the old component is reused.

If you want to try for yourself, checkout my branch ngondestroy-not-triggered and run:

For web:

npm run start 

For nativescript-angular with android:

npm run start.livesync.android

Shouldn’t the two behave the same? So components are destroyed when we navigate from a page and (re)created when we navigate to the page?

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:10
  • Comments:30 (11 by maintainers)

github_iconTop GitHub Comments

18reactions
vakrilovcommented, Nov 2, 2016

Let’s do a little summary so far

Subscriptions And CleanUp

When using page-navigation ngOnInit() will be called the first time the component is navigatedTo and ngOnDestroy() will be called when navigating back from the page with the component (when it is removed form the navigation stack). When navigating forward(deeper in the app) those hooks will not be called. The component instance will be preserved as it is still connected to a page that lives inside the native navigation stack.

However, you might want prevent it from updating while not shown. So far I think @m-abs solution of using page events would be the simplest. You might call the ngOnInit()/ngOnDestroy() hooks directly, but it’s good idea to guard them with a flag to prevent double execution.

Using ActivatedRoute and Params

Normally, in Angular you would inject ActivatedRoute and read parameters from it. Your component will be reused if you do a subsequent navigations to the same route while only changing the params. That’s why params and data inside ActivatedRoute are observables.

In NativeScript when navigating back to an existing page your component will not be re-created. Angular router will still create an new instance ActivatedRoute and put all params in it, but you cannot get hold of it trough injection as no constructor is called.

The Solution: In NativeScript you can inject PageRoute which has an activatedRoute: Observable<ActivatedRoute> inside it. Each time a new ActivatedRoute instance is created for this re-used component it will be pushed in this observable, so you can still get you params:

So, instead of:

class MyComponent {
  id: number;
  constructor(private route: ActivatedRoute) {
    this.route.params
      .forEach((params) => { this.id = +params['id']; });
    }
}

You do:

class MyComponent {
  id: number;
  constructor(private pageRoute: PageRoute) {
    // use switchMap to get the latest activatedRoute instance
    this.pageRoute.activatedRoute
      .switchMap(activatedRoute => activatedRoute.params)
      .forEach((params) => { this.id = +params['id']; });
  }
}

Why So Complicated

Native platforms preserve the views in the native navigation stack when navigating deeper. So we need to preserve the components that are connected to those views too. This is done in the <page-router-outlet>. However, there are some limitations in terms of how Nativescript plugs into the router live-cycle explained in angular/angular#7757. I hope in future version of angular these will be resolved so we can narrow the gap between using <page-router-outlet> and the stock <router-outlet>

11reactions
m-abscommented, Aug 2, 2016

@vakrilov Thank you for the suggest, I’ll try it when I get back to work 😃

I’ll can try to elaborate, sorry for the long text 😃

Our project is based on angular2-seed-advanced, so we share code between the {N} -version and the web-version of our project. The project is an audiobook-player that shows HTML-content synced to the audio during playback. We render the HTML-content with our content-viewer-component. On web we use the component as is and in {N} it runs in a small ng2-webapp inside a web-view. Our page-component sets up events and subscriptions to make this work in our ngOnInit() hook and they’re removed in our ngOnDestroy()hook.

Since the page-component lives on after navigation from it, the subscriptions and events created in ngOnInit() will still be active, this means that the web-view/content-viewer-component still does work that is now unneeded. We also risk having two instances of the web-view/content-viewer-component running at the same time. If the user navigates to the page again without going back, since that won’t reuse the component.

I also think the behavior goes against what the ng2-docs imply.

From: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#hooks-overview

hook Purpose
ngOnInit() Initialize the directive/component after Angular initializes the data-bound input properties.
ngOnDestroy() Cleanup just before Angular destroys the directive/component. Unsubscribe observables and detach event handlers to avoid memory leaks.

From: https://angular.io/docs/ts/latest/guide/router.html#!#route-parameters

Observable params and component re-use

In this example, we subscribe to the route params Observable. That implies that the route params can change during the lifetime of this component.

They might. By default, the router reuses a component instance when it re-navigates to the same component type without visiting a different component first. The parameters can change between each re-use.

We can of cause use the Router.events observable as a workaround, but I’d much rather just rely on the hook ng2 components have for it. I’d also prefer the same behavior (where possible) on both web and in {N}, otherwise it can be surprising for the developers.

I can see it’s done intentionally here: https://github.com/NativeScript/nativescript-angular/blob/master/nativescript-angular/router/page-router-outlet.ts#L112 And I do understand the benefits of caching components like that, but what about larger app with many pages where the user don’t necessarily navigates back? Don’t we risk it accumulates old components and thus causing the leaks the ng2-docs mention?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Force Component Destroy by Using Page Life Cycle in ...
In NativeScript Angular applications, you have components which live inside pages. Each page has its own lifecycle hooks and each component ...
Read more >
Difference between using a service (Angular ... - Stack Overflow
Angular Services save data in the application life cycle, it means if you close the app, the data will not exist anymore.
Read more >
Application Lifecycle - NativeScript Docs
The component lifecycle is controlled by the Angular application. It creates, updates and destroys components. Lifecycle hooks are used to handle different ...
Read more >
Angular Native - NativeScript with Angular 2 - MobiDev
In ReactNative it's necessary to use JSX, which leads to duplications of code of components for different platforms. As opposed to that, in ......
Read more >
What is the difference between AngularJS and Angular 2?
It's an open-source framework that uses HTML as its template language. It was developed to more easily create dynamic web apps through its...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found