How to catch navigating away from page (Angular, ios)


#1

I want to know programmatically when the user navigates away from a page in Nativescript Angular.

This is most important for me when the user taps the “back” button, so if it is easier to be more targeted to catching the back button event, that would be helpful too. I most care about iOS (bc there are other solutions in android).

How can I do it?

I’ve seen variations on this question asked before, with some answers, like here:


and here:

There’s also a blog post generally about navigation events here: https://pointdeveloper.com/nativescript-page-navigation-events/

But I haven’t been able to quite get these to work–they are either older or focused on Vanilla js.

Previously, I have tried to remove the default back button and put in my own, which allows me to then catch the tap event, like so:

<ActionBar class="action-bar">
      <NavigationButton visibility="collapsed"></NavigationButton>
     <ActionItem text="< Back" ios.position="left" (tap)="backClicked()"></ActionItem>
    <Label class="action-bar-title" text="Page Title"></Label>
</ActionBar>
...
then in TS navigate to your chosen page in the backClicked() function.

This works ok, except it changes the behavior of the other pages. (for example, when you hit this back button and go to the selected page, THAT page’s back button will return you to the first page. This actually is different than the default iOS “Back” button, which keeps going back to the beginning of the app.)

So, anyone know how to do the latest and greatest for catching navigating away from a page, or catching the tap of the back button?


#2

Bump. Is there anyway in Nativescript angular to catch when a user navigates away from a page?


#3

You can still get the page instance in Angular by injecting it in constructor and use same events. Or you can think of using Angular’s router events listed here.


#4

I’ve used this in an app to so something depending on the page the user went back to:

  ngAfterViewInit(): void {
    if (application.android) {
      application.android.on(AndroidApplication.activityBackPressedEvent, (data: AndroidActivityBackPressedEventData) => {
        if (this.router.isActive("/intro", true)) {
          // something
        } else {
          // something else
        }
      });
    }
  }

  ngOnDestroy(): void {
    if (application.android) {
      application.android.removeEventListener(AndroidApplication.activityBackPressedEvent);
    }
  }

#5

Thanks. I’ll look into that. Is this just something you have used on android–seems to be android specific code.


#6

Well, the activityBackPressedEvent is Android specific because iOS doesn’t have a hardware back button.


#7

Thanks for the suggestions. Best solution for me so far is following Angular’s router events. Thanks for the suggestion, @manojdcoder .

Some details here: https://angular.io/api/router/Event , here: https://angular.io/api/router/NavigationStart and here: https://stackoverflow.com/questions/37977428/how-to-use-new-navigationstart-angular-router-3-0-0-alpha

The following code works for me to catch when a user leaves a page:

import { Router, NavigationStart } from "@angular/router";
....  
constructor(private router: Router) {
     router.events.subscribe(event => {
      if(event instanceof NavigationStart) {
        console.log('now you are navigating out!')
        // other options include NavigationEnd, NavigationCancel, etc..., per: https://angular.io/api/router/Event      }
    })
}

#8

Just to update here, my previous solution of using router.events … NavigationStart is not quite right for what I had in mind.

If the goal is to detect when the user clicks the back button on a page, the NavigationStart method is overly broad, because it picks up any navigation event.

Additionally, it seems to pick up navigating to the component where the code appears (not just navigating away from that component).

So I am still pursuing a straightforward way of picking up when a user hits the back button on iOS.


#9

My current best for catching when leaving a page (and only when leaving a page–instead of also including navigating to a page) is the ‘unloaded’ event:

import { Component, OnInit } from "@angular/core";
import {Page} from "tns-core-modules/ui/page";
...
export class CoolComponent implements OnInit

constructor (private page: Page){
}

ngOnInit(): void {
  this.page.on('unloaded', (data) => { 
   console.log('you have now left the page!')
  })
}

The caveat is that the unloaded event seems to fire only after the page is fully gone–so it will fire when the user is on the new page. There should be some event that fires at the time the page is preparing to unload, but I have not located it yet.

Additionally, I have tried to figure out how to isolate the Back button press on ios.

Here’s a description of my method:

The unloaded event will fire after you have navigated away from the page for any reason–could be the Back button, or could be other navigating buttons that have been pressed.

The trick to know when the back button is tapped then is to know when 1) the unloaded event has fired and 2) NOT because of the other buttons being tapped.

The way I am doing this is to have an ngIf = notBackButton, which starts as false, but then goes to true if the user hits one of the OTHER navigation buttons.
So, if the unloaded event fires + notBackButton = false, then I know it is the Back button that’s been tapped.

One wrinkle is that in iOS, if a user navigates away from the page because of another button (so notBackButton = true), and then on the second page hits the back button to go back to the main page, it saves the state and notBackButton will still = true.

So I have to have some method of returning the state to notBackButton = false when the user returns to the page.

I do that by refreshing the variable when the .onPopState() event fires, which seems to pick up when navigating to or from the page hitting the back button. (so it will fire if a user on page 2 hits the back button on that page to then go back to the main page–it will also fire when we leave the main page via the back button, which is overly broad, but doesn’t hurt.)

Other methods, like the router.events…instanceof NavigationStart mentioned earlier might work too.

Confusing?

NOTE: I am finding some weird behaviors with onPopState. It seems to multiply on each use, and also pick up action on non-selected pages that have onPopState on them. So use with caution. Probably a better way to do refresh the state of notBackButton exists. I have asked here

Here’s some example code:

html:

<ActionBar class="action-bar">
    <ActionItem text="OtherNavButton" ios.position="right" (tap)="pageForward()"></ActionItem>
</ActionBar>

<StackLayout>
   ...
</StackLayout>

ts:

import { Component, OnInit, } from "@angular/core";
import { Router} from "@angular/router";
import { PlatformLocation } from '@angular/common';
import {Page} from "tns-core-modules/ui/page";

export class CoolComponent implements OnInit {
...
public notBackButton = false; /*meaning that starting assumption is that if navigate away, back button was pressed*/

constructor (private router: Router, private location: PlatformLocation,  page: Page){
 
}

ngOnInit(): void {
   this.location.onPopState(() => {
      this.notBackButton = false; //to reset page to start that notBackButton = false
    }) 

 this.page.on('unloaded', (data) => { 
   console.log('you have now left the page!')
   if (this.notBackButton != true) {
        console.log('back button has been pressed! At least we know this if the user tapped something on the page before navigating away')
   }
  })
}


pageForward(){
   this.notBackButton = true
   this.router.navigate(["forwardPage"])}
}

#10

Best method I can find: ‘navigatingTo’ and ‘navigatingFrom’. Looks like @Eddy pointed the way on stackoverflow

This code seems to work to catch navigating to and also navigating away from a page:

import { Component, OnInit } from "@angular/core";
import {Page} from "tns-core-modules/ui/page";
...
export class CoolComponent implements OnInit {
constructor(private page: Page){

}

  ngOnInit(): void {
   this.page.on('navigatingTo', (data) => {  
     console.log('now going TO the page')
   })
   this.page.on('navigatingFrom', (data) => {  
     console.log('now going AWAY from the page')
   })

  }

#11

Using the ‘navigatingTo’ and ‘navigatingFrom’ solution to isolate the back button would be like the following:

[EDIT: after discussion here , I prefer “navigatedTo” instead of “navigatingTo”]

html:

<ActionBar class="action-bar">
    <ActionItem text="OtherNavButton" ios.position="right" (tap)="pageForward()"></ActionItem>
</ActionBar>

<StackLayout>
   ...
</StackLayout>

ts:

import { Component, OnInit, } from "@angular/core";
import { Router} from "@angular/router";
import { PlatformLocation } from '@angular/common';
import {Page} from "tns-core-modules/ui/page";

export class CoolComponent implements OnInit {
...
public notBackButton = false; /*meaning that starting assumption is that if navigate away, back button was pressed*/

constructor (private router: Router, page: Page){
 
}

ngOnInit(): void {
   this.page.on('navigatedTo', (data) => {
     this.notBackButton = false;
   })

 this.page.on('navigatingFrom', (data) => { 
     if (this.notBackButton != true) {
           console.log('Back Button pressed!')
      }
   })
}

pageForward(){
   this.notBackButton = true
   this.router.navigate(["forwardPage"])}
}