8

I am working in my Ionic 4 app and When the user clicks 2 times on the mobile back button, then it should close the app but this is not happening.

This is my app.component.ts:

lastTimeBackPress = 0;
timePeriodToExit = 2000;
@ViewChildren(IonRouterOutlet) routerOutlets: QueryList<IonRouterOutlet>;

constructor(){
this.backButtonEvent();
}

backButtonEvent() {
    document.addEventListener("backbutton", () => { 
      this.routerOutlets.forEach((outlet: IonRouterOutlet) => {
        if (outlet && outlet.canGoBack()) {
            outlet.pop();
        } else if (this.router.url === '/tabs/tab1') {
          if (new Date().getTime() - this.lastTimeBackPress < this.timePeriodToExit) {
            navigator['app'].exitApp(); //Exit from app
            } else {
            this.presentAlertConfirm();
            this.lastTimeBackPress = new Date().getTime();
          }
          // navigator['app'].exitApp(); // work for ionic 4
        }
      });
    });
  }

  async presentAlertConfirm() {
    const alert = await this.alertController.create({
      // header: 'Confirm!',
      message: 'Are you sure you want to exit the app?',
      buttons: [
        {
          text: 'Cancel',
          role: 'cancel',
          cssClass: 'secondary',
          handler: (blah) => {
          }
        }, {
          text: 'Close App',
          handler: () => {
            navigator['app'].exitApp();
          }
        }
      ]
    });

    await alert.present();
  }

This is working when I am on front page(Tab1) and when I am on the other tabs it is not working and not going to the front page.

I think the problem is in my (outlet && outlet.canGoBack()) because this is not working. I am using the tab theme and Can I send the route to the main tab when the user is no other tabs and clicks the hardware back button.

I am using Ionic 4 tab theme.

Any help is much appreciated.

Rahul Pamnani
  • 1,397
  • 5
  • 26
  • 66

7 Answers7

7

Try This:

lastTimeBackPress = 0;
timePeriodToExit = 2000;
@ViewChildren(IonRouterOutlet) routerOutlets: QueryList < IonRouterOutlet > ;
backButtonEvent() {
  document.addEventListener("backbutton", async() => {
    try {
      const element = await this.modalCtrl.getTop();
      if (element) {
        element.dismiss();
        return;
      }
    } catch (error) {
      console.log(error);
    }
    this.routerOutlets.forEach(async(outlet: IonRouterOutlet) => {
      if (this.router.url != '/tabs/tab1') {
        await this.router.navigate(['/tabs/tab1']);
      } else if (this.router.url === '/tabs/tab1') {
        if (new Date().getTime() - this.lastTimeBackPress >= this.timePeriodToExit) {
          await this.presentAlertConfirm();
          this.lastTimeBackPress = new Date().getTime();
        }
        navigator['app'].exitApp(); // work for ionic 4
      }
    });
  });
}

And call this function in the constructor. This solved my problem because I am using the tabs theme and outlet.pop(); was not working. So I tried this method.

Fabian N.
  • 3,807
  • 2
  • 23
  • 46
Rahul Pamnani
  • 1,397
  • 5
  • 26
  • 66
  • for me this.routerOutlets.forEach(async (outlet: IonRouterOutlet) => {}) is not working. – Arj 1411 Apr 04 '19 at 11:09
  • Whats the difference between ViewChildren and ViewChild – Arj 1411 Apr 04 '19 at 12:39
  • @AnandRaj. Please refer this: https://stackoverflow.com/questions/34326745/whats-the-difference-between-viewchild-and-contentchild – Rahul Pamnani Apr 04 '19 at 17:48
  • 1
    If you use `this.platform.backButton.subscribeWithPriority(0, () => {...})` you can skip all the manual closing of modals, see here: https://stackoverflow.com/a/51728578/2422125 Also using `document.addEventListener("backbutton")` directly is not recommended – Fabian N. Apr 19 '19 at 22:04
  • @FabianN.Okay, so what you suggest for this? – Rahul Pamnani Apr 20 '19 at 03:21
5

Do it this Way.

constructor(private platform: Platform) {
  this.platform.backButton.subscribe(() => {

  });
}
magic.77
  • 829
  • 1
  • 11
  • 21
5

In response to @Raghav comment, I would try it like this:

lastTimeBackPress = 0;
timePeriodToExit = 2000;
@ViewChildren(IonRouterOutlet) routerOutlets: QueryList < IonRouterOutlet > ;

constructor(private platform: Platform) {
  this.backButtonEvent();
}

backButtonEvent() {
  this.platform.backButton.subscribeWithPriority(0, () => {
    this.routerOutlets.forEach(async(outlet: IonRouterOutlet) => {
      if (this.router.url != '/tabs/tab1') {
        await this.router.navigate(['/tabs/tab1']);
      } else if (this.router.url === '/tabs/tab1') {
        if (new Date().getTime() - this.lastTimeBackPress >= this.timePeriodToExit) {
          this.lastTimeBackPress = new Date().getTime();
          this.presentAlertConfirm();
        } else {
          navigator['app'].exitApp();
        }
      }
    });
  });
}

async presentAlertConfirm() {
  const alert = await this.alertController.create({
    // header: 'Confirm!',
    message: 'Are you sure you want to exit the app?',
    buttons: [{
      text: 'Cancel',
      role: 'cancel',
      cssClass: 'secondary',
      handler: (blah) => {}
    }, {
      text: 'Close App',
      handler: () => {
        navigator['app'].exitApp();
      }
    }]
  });

  await alert.present();
}
Fabian N.
  • 3,807
  • 2
  • 23
  • 46
4

That is because you are calling the registerBackButtonAction before platform is ready. You have to subscribe to the backbutton after the platform is ready. An approaching:

this.platform.ready().then(
  () => {
    this.platform.registerBackButtonAction(() => {
      this.platform.exitApp();
   });
  }
);
Adrian S.
  • 41
  • 3
3

try this in your app.component.ts

  lastTimeBackPress = 0;
  timePeriodToExit = 2000;
 @ViewChild(IonRouterOutlet, { static: false }) routerOutlets: IonRouterOutlet

constractor( private router: Router, private alertController: AlertController){this.backbutton()}


backbutton() {
console.log('backbutton')
document.addEventListener("backbutton", () => {
  console.log('backbutton1')
  if (this.routerOutlets && this.routerOutlets.canGoBack()) {
    this.routerOutlets.pop();
  }
  // else if (this.router.url != '/tabs/tabs/tab1') {
  //   this.router.navigate(['/tabs/tabs/tab1']);
  // } 
  else if (this.router.url === '/home') {
    if (new Date().getTime() - this.lastTimeBackPress >= this.timePeriodToExit) {
      this.lastTimeBackPress = new Date().getTime();
      this.presentAlertConfirm();
    } else {
      navigator['app'].exitApp();
    }
  }
});
  }



 async presentAlertConfirm() {
const alert = await this.alertController.create({
  // header: 'Confirm!',
  message: 'Are you sure you want to exit the app?',
  buttons: [{
    text: 'Cancel',
    role: 'cancel',
    cssClass: 'secondary',
    handler: (blah) => { }
  }, {
    text: 'Close App',
    handler: () => {
      navigator['app'].exitApp();
    }
  }]
});
await alert.present();
  }
naveen pandi
  • 595
  • 6
  • 14
  • 1
    Please [edit] with more information. Code-only and "try this" answers are discouraged, because they contain no searchable content, and don't explain why someone should "try this". We make an effort here to be a resource for knowledge. Further explanation would help people understand why your solution is good one and how it works. – Brian Tompsett - 汤莱恩 Sep 05 '19 at 13:05
1

You can also try the following code snippets,

Make a change or add code like this in your app.component.ts file

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

import { Platform, IonRouterOutlet, ToastController } 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',
  styleUrls: ['app.component.scss']
})
export class AppComponent {
  //code for exit app
  // set up hardware back button event.
  lastTimeBackPress = 0;
  timePeriodToExit = 2000;

  //code for exit app
  @ViewChildren(IonRouterOutlet) routerOutlets: QueryList<IonRouterOutlet>;
  constructor(
    private platform: Platform,
    private splashScreen: SplashScreen,
    private statusBar: StatusBar,
    private toastController: ToastController,
    private router: Router
  ) {
    this.initializeApp();
    // Initialize BackButton Eevent.
    this.backButtonEvent();
  }

  initializeApp() {
    this.platform.ready().then(() => {
      this.statusBar.styleLightContent();
      this.splashScreen.hide();
    });
  }
  
  // active hardware back button
  backButtonEvent() {
    this.platform.backButton.subscribe(async () => {

      this.routerOutlets.forEach(async (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 {
            const toast = await this.toastController.create({
              message: 'Press back again to exit App.',
              duration: 2000,
              position: 'middle'
            });
            toast.present();
            // console.log(JSON.stringify(toast));
            this.lastTimeBackPress = new Date().getTime();
          }
        }
      });
    });
  }
}

Make a change or add code like this in your home.ts file or where you want to user exit from your app page.

import { Component } from '@angular/core';
import { Platform } from '@ionic/angular';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  public subscription: any;

  constructor(private platform: Platform) { }

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

  ionViewWillLeave() {
    this.subscription.unsubscribe();
  }
}
user9088454
  • 1,076
  • 1
  • 15
  • 45
1

Try this

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

import {IonRouterOutlet, Platform, ToastController} 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 {Location} from '@angular/common';

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

    lastTimeBackPress = 0;
    timePeriodToExit = 2000;

    // @ts-ignore
    @ViewChildren(IonRouterOutlet) routerOutlets: QueryList<IonRouterOutlet>;

    constructor(
        private platform: Platform,
        private splashScreen: SplashScreen,
        private statusBar: StatusBar,
        private toastController: ToastController,
        private router: Router,
        private location: Location
    ) {
        this.initializeApp();
    }

    initializeApp() {
        this.platform.ready().then(() => {
           
            this.backButtonEvent();
        });
    }

    // active hardware back button
    backButtonEvent() {
        this.platform.backButton.subscribeWithPriority(0, () => {
            this.routerOutlets.forEach(async () => {
                if (this.router.url !== '/tabs/home') {
                    await this.location.back();
                } else {
                    if (new Date().getTime() - this.lastTimeBackPress < this.timePeriodToExit) {
                        navigator['app'].exitApp(); // work in ionic 4
                    } else {
                        const toast = await this.toastController.create({
                            message: 'Press back again to exit App.',
                            duration: 2000,
                            position: 'middle'
                        });
                        toast.present();
                        // console.log(JSON.stringify(toast));
                        this.lastTimeBackPress = new Date().getTime();
                    }
                }
            });
        });
    }
}
inuryyev
  • 143
  • 2
  • 10