Ionic 4 ngOnDestroy not called from component on page navigation

Hello,

I have an ionic 4 app. I am using angular routing.

I have a page with a number of @Components on it.

In one component I want ngOnDestroy called when navigating to another page.

In my app.component.ts I have a menu which I can chose which page to navigate to.

this.router.navigate([page.url]);

When I navigate from the page with the component that has the ngOnDestroy( ), ngOnDestroy( ) is not called.

Any ideas what I am doing wrong?

Thanks

Did you implement OnDestroy?

import { Component, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-test',
  templateUrl: './test.page.html',
  styleUrls: ['./test.page.scss'],
})
export class TestPage implements OnDestroy {

  constructor() { }

  ngOnDestroy() {
    // Do something on page destroy
  }

}

Yes I did.

If I close the app I can see in chrome inspect that the ngOnDestroy is called.

It just is not called when navigating to another page.

Hello again, after some research I have found this post on StackOverflow in which is explained why ngOnDestroy doesn’t work on ionic and you should use ionViewDidLeave instead. Sorry for my other post.

Thanks for the comments. I saw that post too. Unfortunately those lifecycle methods - like ionViewDidLeave() - will not be called from a component. ionViewDidLeave() will be called from the page/view containing the component.

In my case I have a home page with multiple components. The home page’s ionViewDidLeave() is called when the angular router navigates to a different page. In one of my components I have a timer that is making periodical calls that I want to delete when I navigate to another page. Since it is a component, ionViewDidLeave() is not called nor ngOnDestroy(). Again in chrome I can see the component’s ngOnDestroy() being called when I close the app but not when navigating to another page/view.

<ion-content>
    <status-one-card></status-one-card>
    <status-two-card></status-two-card>
    <status-three-card></status-three-card>
</ion-content>

Nice read on hooks

Thanks for this. I have read it. Maybe I missed something but I did not see where it addresses the case where you want to clean up a component which is part of a page/view prior to navigating to another page/view.

In my case I have a component that is part of a page that does various things. One is it has a timer which I want to delete when navigating to another page/view. Otherwise it keep firing until the app closes.

I tried all of these - ionViewWillEnter, ionViewDidEnter, ionViewWillLeave, ionViewDidLeave in my @Component (status-two-card below). None were called in the component that is part of the home page/view. All of them are called from the home page/view. Unfortunately the home page/view does not have a reference to the timer I started in the component (status-two-card).

home.page.html

<ion-content>
    <status-one-card></status-one-card>
    <status-two-card></status-two-card>
    <status-three-card></status-three-card>
</ion-content>

My current work around is having the following in the component’s constructor (status-card-two) . It will check any change in the url and if it is not on the home page (i.e. navigating to another page) it will clear out the timer associated with the component.

router.events.subscribe((data: any) => {
  if (data.url !== '/home') {
    clearTimeout(this.timer);
  }
});

Any ideas on a better approach or what I might be doing wrong with the life cycle methods?

Thanks

Also found this earlier post:

An inner component knows nothing about Ionic lifecycle events. Ionic/pages will not notify inner components about loading, become the active page, etc. If you need your inner component to do something based on lifecycle events, tie the parent lifecycle event with the inner component using Angular’s @ViewChild decorator.

In parent component get a reference to the child component using @ViewChild as explained here . Then, inside the parent lifecycle event, call a public function defined in child component.

Seems to me the parent needs to communicate with the component to instruct a page change or other user interaction which leads to the desired reset.

An rxjs based timer service may be a better bet

While your solution may work you may feel correctly it seems a bit awkward, although there may be use cases to do so

https://angular.io/guide/component-interaction

This is because when you call this.router.navigate([page.url]); you are actually pushing onto the navigation stack and not destroying the component. instead add a NavController to your constructor like:

 constructor(
    public navCtrl: NavController
  ) {
  }

then call the navigateRoot in your menu navigation method:

  private navigate(url: string) {
    this.navCtrl.navigateRoot(url);
  }

This removes any other component from the nav stack and will trigger the ngOnDestroy Hook. This will cause you to loose the ‘forward’ transition animation, but will fix the overall issue of ngOnDestroy not firing.

2 Likes

if you prefer to use the Angular way, adding { replaceUrl: true } as NavigationExtra:

this.router.navigate([page.url], { replaceUrl: true });

or on template

<a [routerLink]="page.url" replaceUrl="true">...

In my solution I add a dependency in the child components on ViewController. this can be @Optional if you will ever use the component outside of ionic pages. As a child of an ionic page, it gets the ViewController of the hosted page. You can then subscribe to the page lifecycle events of that view controller for leaving the page and other non-destructive events.