Search code examples
androidangularjsionic4

How to add a page to navigation history to prevent app from exiting?


I'm still rather new to Ionic 4. I'm making an App that receives push notification. The navigation inside the app works like this:

Home page -> Detail page 

Every time the user taps on the notification, the app will open and navigates to Detail page. The navigation works but since the navigation history is empty, if the user taps on the hardware back button, the app exits. I want it to redirect the user to Home page instead.

How do I achieve this in Ionic 4? is there any way to push a page to navigation history? I have read the documentation but couldn't find anything about this. The closest was probably NavCtrl.push() but it's no longer usable in Ionic 4.

Thank you.


Solution

  • There may be an easier way to do this but the following approach is a very flexible one because it'd allow you to run any custom logic when the user wants to go back from the page shown after a push notification or a deep link is opened.

    Please take a look at this StackBlitz demo.

    StackBlitz demo

    Please notice that in the demo, I'm redirecting to the DetailsPage as soon as the app is loaded because of the following code from the app-routing.module file:

    {
      path: "",
      redirectTo: "/details/1?fromDeepLink=true", // <-- here!
      pathMatch: "full"
    }
    

    Anyway, the important part happens in the DetailsPage. There, you need to handle what happens when the user tries to go back using a) the back button from the header and b) the physical back button from Android devices

    The code is pretty self-explanatory, but basically in that page I'm looking for the fromDeepLink query string param, and if it's true, the app will register a custom action for both the back button from the header and for the physical back button from Android devices.

    The custom action sets the HomePage as the root page, but sets the animationDirection parameter to be 'back'. That way it'd look like the user is going back to that page even if we're actually adding it to the navigation stack.

    It's important to notice that this custom handler is being removed as soon as the user leaves the page so that we don't affect the default behaviour of the back button in any other pages.

    import { Component, OnInit, ViewChild } from "@angular/core";
    import { ActivatedRoute } from "@angular/router";
    
    import { IonBackButtonDelegate, NavController,  Platform,  ViewDidEnter, ViewWillLeave } from "@ionic/angular";
    
    import { Subscription } from "rxjs";
    
    @Component({
      selector: "app-details",
      templateUrl: "./details.page.html",
      styleUrls: ["./details.page.scss"]
    })
    export class DetailsPage implements OnInit, ViewDidEnter, ViewWillLeave {
      @ViewChild(IonBackButtonDelegate, { static: false })
      public backButton: IonBackButtonDelegate;
    
      public itemId: number;
      public fromDeepLink: boolean = false;
    
      private unregisterBackButtonAction: Subscription;
    
      constructor(
        private platform: Platform,
        private route: ActivatedRoute, 
        private navCtrl: NavController,
      ) {}
    
      ngOnInit() {
        const itemIdParam = this.route.snapshot.paramMap.get("itemId");
        const fromDeepLinkParam = this.route.snapshot.queryParamMap.get('fromDeepLink');
    
        this.itemId = +itemIdParam;
        this.fromDeepLink = fromDeepLinkParam 
          ? fromDeepLinkParam.toLocaleLowerCase() === 'true' 
          : false;
      }
    
      ionViewDidEnter() {
        if(this.fromDeepLink) {
          this.initializeCustomBackButtonAction()
        }
      }
    
      ionViewWillLeave() {
        if(this.fromDeepLink) {
          this.removeCustomBackButtonAction();
        }
      }
    
      private initializeCustomBackButtonAction(): void {
        const leavingCallback = () => {
          console.log('Using custom back button action');
          this.navCtrl.navigateRoot('/home', { animationDirection: 'back' });
        };
    
        // Override the back button from the header
        if (this.backButton) {
          this.backButton.onClick = leavingCallback;
        }
    
        // Override the physical back button from Android devices
        this.unregisterBackButtonAction = this.platform.backButton.subscribeWithPriority(10, leavingCallback);
      }
    
      private removeCustomBackButtonAction(): void {
        this.unregisterBackButtonAction?.unsubscribe();
      }
    }
    
    

    Please also notice that by default the ion-back-button is not shown if there's no page before the current page in the navigation stack, so in the demo I'm setting the defaultHref property like this:

    <ion-back-button defaultHref></ion-back-button>
    

    I'm leaving it empty because the component is actually going to override what this back button does with my custom back button action. But the defaultHref needs to be added to the template, otherwise the back button won't be shown.