20

Please find the below code for the Android hardware back button action in ionic3. As Ionic4 uses angular routing for navigation how the pop event will take place for the back button? If we want to pop to the last page we can use the following code this.navCtrl.goBack('/products');. But how we can use it for the android hardware back button action in ionic4?

Ionic3 hardware back button action

this.platform.registerBackButtonAction(() => {
    let activePortal = this.ionicApp._loadingPortal.getActive() ||
        this.ionicApp._modalPortal.getActive() ||
        this.ionicApp._toastPortal.getActive() ||
        this.ionicApp._overlayPortal.getActive();
    if (activePortal) {
        activePortal.dismiss();
    } else {
        if (this.nav.canGoBack()) {
            ***this.nav.pop();***
        } else {
            if (this.nav.getActive().name === 'LoginPage') {
                this.platform.exitApp();
            } else {
                this.generic.showAlert("Exit", "Do you want to exit the app?", this.onYesHandler, this.onNoHandler, "backPress");
            }
        }
    }
});
Fabian N.
  • 3,807
  • 2
  • 23
  • 46
Arj 1411
  • 1,395
  • 3
  • 14
  • 36

6 Answers6

23

Update: This was fixed in v4.0.0-beta.8 (dfac9dc)


Related: ionic4 replacement for registerBackButtonAction


This is tracked on GitHub, in the Iconic Forum and Twitter
Until there is an official fix, you can use the workaround below.


Using platform.backButton.subscribe (see here) platform.backButton.subscribeWithPriority(0, ...) to let ionic handle closing all the modals/alerts/... , the code ionic uses when its own back button is pressed and the new router-controller together we get something like this:

import { ViewChild } from '@angular/core';
import { IonRouterOutlet, Platform } from '@ionic/angular';
import { Router } from '@angular/router';

//...

/* get a reference to the used IonRouterOutlet 
assuming this code is placed in the component
that hosts the main router outlet, probably app.components */
@ViewChild(IonRouterOutlet) routerOutlet: IonRouterOutlet;

constructor(
  ...
  /* if this is inside a page that was loaded into the router outlet,
  like the start screen of your app, you can get a reference to the 
  router outlet like this:
  @Optional() private routerOutlet: IonRouterOutlet, */
  private router: Router,
  private platform: Platform
  ...
) {
  this.platform.backButton.subscribeWithPriority(0, () => {
    if (this.routerOutlet && this.routerOutlet.canGoBack()) {
      this.routerOutlet.pop();
    } else if (this.router.url === '/LoginPage') {
      this.platform.exitApp(); 

      // or if that doesn't work, try
      navigator['app'].exitApp();
    } else {
      this.generic.showAlert("Exit", "Do you want to exit the app?", this.onYesHandler, this.onNoHandler, "backPress");
    }
  });
}
Fabian N.
  • 3,807
  • 2
  • 23
  • 46
  • Hi @Fabian, Thanks for your updates, but I have some questions. First, when I debugging ionBackButton directive the canGoBack function is always return false, so how can I enable it ? my second question, How can I handle back button Programmatically ?, because I wan to handle some cases before go back. Thanks, – Bassam Nov 21 '18 at 08:54
  • @Fabian, Your solution is partially working. Application is not exit even from the root page – Arj 1411 Apr 06 '19 at 14:55
  • @AnandRaj try replacing it with `navigator['app'].exitApp();` – Fabian N. Apr 06 '19 at 15:47
  • Take a look here, https://stackoverflow.com/questions/27137119/where-is-navigator-app-documentation to be honest I never questioned its validity because it worked on all devices I tested it on. – Fabian N. Apr 06 '19 at 15:52
  • Be honest the navigator['app'].exitApp() is also not working. – Arj 1411 Apr 06 '19 at 15:55
  • I have to close all the overlaying components. Thats works with @MD.Riyaz 's answer. I want to close the application on root page if there are no overlayings – Arj 1411 Apr 06 '19 at 15:57
  • I don't know why its not working for you. I have my android tablet right in front of me and `navigator['app'].exitApp()` closes my ionic4 app without a problem. – Fabian N. Apr 06 '19 at 15:57
  • Property 'exitApp' does not exist on type 'Platform'. This is what I am getting – Arj 1411 Apr 06 '19 at 15:59
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/191368/discussion-between-fabian-n-and-anand-raj). – Fabian N. Apr 06 '19 at 15:59
  • Can anyone do this for a `React` app pleaaase...? – Dev Yego Oct 19 '20 at 16:42
13

Try This: app.comonent.ts

import { Component, ViewChildren, QueryList } from '@angular/core';
import { Platform, ModalController, ActionSheetController, PopoverController, IonRouterOutlet, MenuController } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { Router } from '@angular/router';
import { Toast } from '@ionic-native/toast/ngx';

@Component({
    selector: 'app-root',
    templateUrl: 'app.component.html'
})
export class AppComponent {

    // set up hardware back button event.
    lastTimeBackPress = 0;
    timePeriodToExit = 2000;

    @ViewChildren(IonRouterOutlet) routerOutlets: QueryList<IonRouterOutlet>;

    constructor(
        private platform: Platform,
        private splashScreen: SplashScreen,
        private statusBar: StatusBar,
        public modalCtrl: ModalController,
        private menu: MenuController,
        private actionSheetCtrl: ActionSheetController,
        private popoverCtrl: PopoverController,
        private router: Router,
        private toast: Toast) {

        // Initialize app
        this.initializeApp();

        // Initialize BackButton Eevent.
        this.backButtonEvent();
    }

    // active hardware back button
    backButtonEvent() {
        this.platform.backButton.subscribe(async () => {
            // close action sheet
            try {
                const element = await this.actionSheetCtrl.getTop();
                if (element) {
                    element.dismiss();
                    return;
                }
            } catch (error) {
            }

            // close popover
            try {
                const element = await this.popoverCtrl.getTop();
                if (element) {
                    element.dismiss();
                    return;
                }
            } catch (error) {
            }

            // close modal
            try {
                const element = await this.modalCtrl.getTop();
                if (element) {
                    element.dismiss();
                    return;
                }
            } catch (error) {
                console.log(error);

            }

            // close side menua
            try {
                const element = await this.menu.getOpen();
                if (element) {
                    this.menu.close();
                    return;

                }

            } catch (error) {

            }

            this.routerOutlets.forEach((outlet: IonRouterOutlet) => {
                if (outlet && outlet.canGoBack()) {
                    outlet.pop();

                } else if (this.router.url === '/home') {
                    if (new Date().getTime() - this.lastTimeBackPress < this.timePeriodToExit) {
                        // this.platform.exitApp(); // Exit from app
                        navigator['app'].exitApp(); // work in ionic 4

                    } else {
                        this.toast.show(
                            `Press back again to exit App.`,
                            '2000',
                            'center')
                            .subscribe(toast => {
                                // console.log(JSON.stringify(toast));
                            });
                        this.lastTimeBackPress = new Date().getTime();
                    }
                }
            });
        });
    }
}

it's work for me, in ionic v4 beta

Fabian N.
  • 3,807
  • 2
  • 23
  • 46
MD.Riyaz
  • 412
  • 3
  • 9
  • Does this only work for hybrid apps, which are deployed to the app store? So it won't work at a PWA? Because I can't get it working – Roman Oct 09 '18 at 17:40
  • @MD.Riyaz you can use `backButton.subscribeWithPriority(0, ...)` to let ionic close all the modals/... for you. – Fabian N. Apr 06 '19 at 17:38
  • 1
    `outlet.canGoBack()` always return false... Even when navigating with `navCtrl.navigateForward('...')` – Miquel Apr 07 '19 at 23:59
  • 1
    @Miquel I have a workaround for angular router based navigation with only one outlet: I saved the url of the root page `setTimeout(() => this.rootPage = this.router.url)` in my app components constructor and then compared the current location to this url to find out if I'm back to square one `if (this.router.url == this.rootPage) { this.exit(); } else { this.location.back(); }` – Fabian N. Apr 09 '19 at 14:18
  • @MD.Riyaz this.menu.getOpen() will return undefined on root page where the menu is disabled – Arj 1411 Apr 10 '19 at 06:49
  • this.platform.backButton.subscribeWithPriority(0,async () => { }); I used its working for me – Nitin Karale Aug 25 '19 at 18:15
  • @MD.riyaz doesn't seem to be working once the app launches you need to navigate to another page first then it starts working – MSD Apr 15 '20 at 13:34
1

This is my working code on the Ionic 5 Project. using Cordova/PhoneGap

import {Component} from '@angular/core';
import {ToastService} from './_services/toast.service';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss']
})
export class AppComponent {
  constructor(private toastCtrl: ToastService) {
    this.backButton();
  }
  backButton() {
      const that = this;
      let lastTimeBackPress = 0;
      const timePeriodToExit = 2000;
      function onBackKeyDown(e) {
          e.preventDefault();
          e.stopPropagation();
          if (new Date().getTime() - lastTimeBackPress < timePeriodToExit) {
              navigator.app.exitApp();
          } else {
              that.presentToast();
              lastTimeBackPress = new Date().getTime();
          }
      }
      document.addEventListener('backbutton', onBackKeyDown, false);
  }
  presentToast() {
    const toast = this.toastCtrl.create({
      message: "Press again to exit",
      duration: 3000,
      position: "middle"
    });
  toast.present();
  }
}
0

this is how i do in my app (developed using ionic4 works in android apps). so when the user click back on android phone, app exits.

sample code:

import { Component, AfterViewInit, OnDestroy } from '@angular/core';
import { Platform } from '@ionic/angular';
@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss']
})
export class AppComponent implements AfterViewInit, OnDestroy {

  constructor(private platform: Platform) { }
  backButtonSubscription;
  ngAfterViewInit() {
    this.backButtonSubscription = this.platform.backButton.subscribe(() => {
      // add logic here if you want to ask for a popup before exiting
      navigator['app'].exitApp();
    });
  }

  ngOnDestroy() {
    this.backButtonSubscription.unsubscribe();
  }
}

source : here

Mateen
  • 1,631
  • 1
  • 23
  • 27
  • This solution works from version ionic/angular 4.7.2 https://github.com/ionic-team/ionic/releases/tag/v4.7.2 I was on 4.6.1 and no events were firing – Daniel S. Nov 06 '19 at 22:51
0

use the cordova event "backbutton"

document.addEventListener("backbutton", youFunction, false);

I use ionic4/vue and it worked

0

Customizing Android Back Button in Ionic 4 ... Each page wise back.

Step 1: import { Platform, NavController } from '@ionic/angular';

Step 2: constructor(public navCtrl: NavController){}

Step 3:

 private async onBack() {
    this.navCtrl.navigateBack('/project-details');
  }

Step 4:

 this.platform.backButton.subscribe(()=>{
        this.onBack();
      });
SHUBHASIS MAHATA
  • 873
  • 7
  • 12