Hardware back button with Ionic 4

@cherzog that’s exactly what I’m using. When I inspect the device via devtools and go to the console I can see the logs when back button on Android is pressed. I can also minimize the app using AppMinimize.

Maybe it has to do with you using it on a modal page? Modals always seem to have issues. Try doing it in something that isn’t a modal (ie home page or app component like suggested above).

Also, why are you putting this in a modal? I’m having a hard time understanding what a use case would be.

It’s the modal, yes. It also happens when you use another overlay. A select, for example. You have the sub-page open, open the Select and press the Back button. The subpage disappears and the select overlay remains open. I’ve updated the git hub repo …

Am i doing something wrong? In app.component i have the following code, still nothing shows on console in devtools(chrome) when back button pressed on device.

image

UPDATE: This solution actually works, i was just running the app on device in debug mode with “ionic cordova run android --device -l --debug” command and i guess that’s messed it up. After running it without --debug it worked like a charm.

Same here. No event fired.

To be clear: I start “ionic serve” and use the browser on the android device.

Here’s my workaround:

import { ModalController } from '@ionic/angular';
import { Injectable } from '@angular/core';
import { CanDeactivate, Router, ActivatedRouteSnapshot } from '@angular/router';
import { Location } from '@angular/common';

@Injectable({
  providedIn: 'root'
})
export class BackButtonService implements CanDeactivate<any> {

  constructor( 
    private modalCtrl: ModalController,
    private alertCtrl: AlertController,
    private popoverCtrl: PopoverController,
    private readonly location: Location,
    private readonly router: Router) { }

  canDeactivate(component: any, currentRoute: ActivatedRouteSnapshot): Promise<boolean> {
    return new Promise((resolve) => {
      this.modalCtrl.getTop()
      .then((modalElement) => {
        if (modalElement) {
          modalElement.dismiss();
          const currentUrlTree = this.router.createUrlTree([], currentRoute);
          const currentUrl = currentUrlTree.toString();
          this.location.go(currentUrl);
          resolve(false);
        } else {
          this.alertCtrl.getTop()
          .then((alertElement) => {
            if (alertElement) {
              alertElement.dismiss();
              const currentUrlTree = this.router.createUrlTree([], currentRoute);
              const currentUrl = currentUrlTree.toString();
              this.location.go(currentUrl);
              resolve(false);
            } else {
              this.popoverCtrl.getTop()
              .then((popoverElement) => {
                if (popoverElement) {
                  popoverElement.dismiss();
                  const currentUrlTree = this.router.createUrlTree([\*], currentRoute);
                  const currentUrl = currentUrlTree.toString();
                  this.location.go(currentUrl);
                  resolve(false);
                } else {
                  resolve(true);
                }
              });
            }
          });
        }
      });
    });
  }
}

In App-routing.module.ts:

  {
    path: 'subpage',
    loadChildren: './subpage/subpage.module#SubpagePageModule',
    canDeactivate: [BackButtonService]
  },

This is enough in my case. You can certainly extend it to other types (menus, alerts, etc).

1 Like

Hai GameAppsBD .This is not working in tabs home page ,But it is working fine in login page.Here is my code

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

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

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

          } else {
            this.showAlert()
              this.lastTimeBackPress = new Date().getTime();
          }
      }
  });
  this.lastBack = Date.now();

});
}

This is not Working in Tabs Home Page.Working Fine in Login Page . Here is my code`.Please Help Me

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

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

          } else  {
              if (new Date().getTime() - this.lastTimeBackPress < this.timePeriodToExit) {
                  navigator['app'].exitApp(); // work in ionic 4

              } else {
                this.showAlert()
                  this.lastTimeBackPress = new Date().getTime();
              }
          }
      });
      this.lastBack = Date.now();
  });
  }

My app doesn’t close when hitting the backButton in the homePage. I tried this :

backButtonEvent(){
       this.platform.backButton.subscribe(()=>{
       navigator['app'].exitApp();
    })

And it works…
But it also works in the other pages, so instead of having the normal “back” behaviour, the backButton exits the app from everywhere in the app…

I had the idea of unsubscribing when moving from the homepage, but the problem is that when I go back to the home page, the ngOnInit is not fired, so I can’t resubscribe…

Another idea is to access the name of the page where the user is (router??) from the backButtonEvent function, to know which behaviour to have

Had you this sort of problem, did you solve it ??

Thanks

Any solution so far? I still have the same problem, being on the root page the back button does not work to close the application, and add navigator [“app”]. exitapp () the application is left on any page that is

I found a solution here :
https://forum.ionicframework.com/t/v4-back-button-doesnt-exit-app-solved-tutorial/149994

Thank you, It’s work me, and i’m doing small changes in my code , change in If condition this.router.isActive(’/home’,true),

ex:
if (this.router.url === ‘/home’)
To
if (this.router.isActive(’/home’,true) )

Dear in my case, this update works perfectlly.

 try {
     const element = await this.menu.getOpen();
     if (element !== null && element !== undefined)
          this.menu.close();
          return;
     }
 } catch (error) {
   }

please note updated line here if (element !== null && element !== undefined) because menu.getOpen() does not return null as per it’s documentation and always exceutes this if block regarless of menu behaviour so code after this try catch block is always skipped. instead its undefined when menu is closed. I think its bug

This may help.

Works fine, but having one issue. Let’s say my App’s home page is Services.

Now I’m checking this 3 case:

  1. If i’m on Services page, pressing back-button then it shows toast. => WORKS AS EXPECTED
  2. If i navigate to Login page from Services, then In Login page, i’m pressing back-button then it goes back to Services page and shows toast. => NOT WORK AS EXPECTED
    (It should not display toast)
  3. If i navigate to Register page from Login, then In Register page, i’m pressing back-button then it goes back to Login page and again it goes back to Services page. => NOT WORK AS EXPECTED
    (It should only navigate to Login page)

Please help ASAP!

Thanks,
Nirav

Can anyone tell me about this line :
@ViewChildren(IonRouterOutlet) routerOutlets: QueryList<IonRouterOutlet>

What is that? Is i have to give id or name to my ion-router-outlet tag?

Add this code in app.component.ts

import { Component, OnInit, ViewChildren, QueryList } from ‘@angular/core’;
import { Platform, IonRouterOutlet } from ‘@ionic/angular’;
import { SplashScreen } from ‘@ionic-native/splash-screen/ngx’;
import { StatusBar } from ‘@ionic-native/status-bar/ngx’;
import { Router } from ‘@angular/router’;

@Component({
selector: ‘app-root’,
templateUrl: ‘app.component.html’,
})

export class AppComponent implements OnInit {

@ViewChildren(IonRouterOutlet) routerOutlets: QueryList;

constructor(
    private platform: Platform,
    private splashScreen: SplashScreen,
    private statusBar: StatusBar,
    public router: Router,
) {
    this.initializeApp();
    this.backButtonEvent();
}

ngOnInit() { }

backButtonEvent() {
    this.platform.backButton.subscribe(() => {
        this.routerOutlets.forEach((outlet: IonRouterOutlet) => {
            if (this.router.url === '/home') {
                navigator['app'].exitApp();
            } else {
                window.history.back();
            }
        });
    });
}

initializeApp() {
    this.platform.ready().then(() => {
        this.statusBar.styleDefault();
        this.splashScreen.hide();
    });
}

}

1 Like

I get error in routerOutlets.forEach.
vendor.js:51643 ERROR Error: Uncaught (in promise): TypeError: this.routerOutlets.forEach is not a function TypeError: this.routerOutlets.forEach is not a function at AppComponent.<anonymous> (main.js:1130) at step (vendor.js:106764) at Object.next (vendor.js:106745) at fulfilled (vendor.js:106735) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (polyfills.js:2749) at Object.onInvoke (vendor.js:53218) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (polyfills.js:2748) at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (polyfills.js:2508) at polyfills.js:3247 at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (polyfills.js:2781) at resolvePromise (polyfills.js:3189) at polyfills.js:3099 at fulfilled (vendor.js:106735) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (polyfills.js:2749) at Object.onInvoke (vendor.js:53218) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (polyfills.js:2748) at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (polyfills.js:2508) at polyfills.js:3247 at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (polyfills.js:2781) at Object.onInvokeTask (vendor.js:53209)

Never mind that, I forgot to change ViewChild to ViewChildren but now I have a problem when i dismiss a page. The outlet.pop() and the Toast function triggers at the same time.

Hi,
All except the part this.routerOutlets.forEach((outlet: IonRouterOutlet) => {}) is working. Control is not moving inside this. Please help

Add this in tab1/Home page

lastTimeBackPress = 0;
timePeriodToExit = 2000;

constructor(){
this.platform.backButton.subscribe(async() => {
if (new Date().getTime() - this.lastTimeBackPress < this.timePeriodToExit) {
navigator[‘app’].exitApp(); // work in ionic 4
} else if (this.router.url === ‘/tabs/tab1’) {
this.matBar.open(‘Press back again to exit App’, ‘2000’, {
duration: 2000,
});
this.lastTimeBackPress = new Date().getTime();
}
});
}

Hi, I have to handle same scenario with hardware back button to exit the app when user clicks back button from home page and should not take it to login page as user already logged In. I tried your solution placing code in app.component.ts but it did not work for me… Could you please help in getting this done by providing sample application code(complete application)
export class AppComponent {
@ViewChild(IonRouterOutlet) routerOutlet: IonRouterOutlet;

constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private router: Router,
private commonService: CommonService
) {
this.initializeApp();
this.platform.backButton.subscribeWithPriority(0, () => {
alert(this.router.url);
if (this.routerOutlet && this.routerOutlet.canGoBack()) {
this.routerOutlet.pop();
} else if (this.router.url === ‘/login’) {
// this.platform.exitApp();
// or if that doesn’t work, try
navigator[‘app’].exitApp();
} else {
this.commonService.showAlert(‘Exit’);
}
});
}