ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
51.1k stars 13.51k forks source link

[v4.0.0-beta.12] When platform hardware backButton subscribed, router.goBack should not be triggered #15820

Closed peterpeterparker closed 6 years ago

peterpeterparker commented 6 years ago

Feature Request

Ionic Info

Ionic Angular v4.0.0-beta.12

Describe the Feature Request In my app, I've got many pages where the back action is overridden. For example, I've got a wizard build on top of ion-slides. There, when the user click "back", he/she don't necessarily want to go back in the navigation but rather want to go back the previous slide

In Ionic v3 registerBackButtonAction allowed me to override the hardware back button. When I was doing so, no extra back action where triggered

In Ionic v4 backButton allows me to override the hardware back button, but doing so, an extra back action is automatically triggered (see the @Listen('document:ionBackButton') in https://github.com/ionic-team/ionic/blob/7d50305b7f6c48392dd278b55824e4c712890214/core/src/components/router/router.tsx and win.document.dispatchEvent(ev); in https://github.com/ionic-team/ionic/blob/c49d896e085278a4ece521a22d983a4fa745adc2/core/src/utils/hardware-back-button.ts)

Summarized of the behavior:

Ionic v3 = Subscribe to back button = custom code/back is executed Ionic v4 = Subscribe to back button = custom code/back is executed + general back executed too

Describe Preferred Solution I see two solutions:

  1. hardware-back-button.ts should not trigger anymore the event ionBackButton letting users implement their own navigation

  2. a new feature or option should be implemented in order to specify, while subscribing to the hardware back button, that the automatic back navigation should not be triggered

Describe Alternatives Don't know maybe there are other solutions workarounds? What I could say is that it's a core need for my app in order to be able to unleash my app on Android

Related Code

Ionic v3

     this.unregisterCustomBackActionFunction = this.platform.registerBackButtonAction(() => {
            if (this.something) {
                this.slider.slidePrev();
            } else {
                this.navController.pop();
            }
        });

Ionic v4

  this.customBackActionSubscription = this.platform.backButton.subscribe(async ($event) => {
          if (this.something) {
             await this.slider.slidePrev();
         else {
             await this.navCtrl.navigateBack('/somewhere');
    });

Additional Context There is a discussion started on the Ionic Forum: https://forum.ionicframework.com/t/ionic-4-prevent-backbutton-from-closing-page

A bit ugly workaround I need this option because my page contains a slider and I want to navigate back with the hardware back button to the previous slide. The trick which seems to work, is to add state in the history, therefore, the automatic back action (see goBack or the ion-router https://github.com/ionic-team/ionic/blob/7d50305b7f6c48392dd278b55824e4c712890214/core/src/components/router/router.tsx) will go back in the navigation history by first popping the states I added

Like

<ion-slides (ionSlideNextEnd)="pushHistoryState()">

async pushHistoryState() {
        const sliderIndex: number = await this.slider.getActiveIndex();
        history.pushState({newAd: sliderIndex}, document.title);
    }
}

also of course you should do the same for the action which would slide to previous slide by poping the state

history.back(1);

Update

Just noticed another use case where having an automatic navigation is not well suited: in case for example you would close a modal with in any case a value. With an automatic navigation executed with the hardware back button, the modal just gonna always be dismissed with no values. So far I noticed 8 modals in my app which are impacted by this

RHinderiks commented 6 years ago

Personally i wish we could override the back button per button since we already have to put them manually in there. Adding [backAction]="customFunction()" or something similar would be great. This function should also be triggered when using the hardware button (or even an option to disable the hardware button but this probably isn't a great idea ?)

peterpeterparker commented 6 years ago

awesome, thx @manucorporat ❤️

RHinderiks commented 6 years ago

With this fix, Whats the best practise to go about implementing custom back button functionality now ?

peterpeterparker commented 6 years ago

@RHinderiks here you go for v4-beta.12, credit @manucorporat too:

@HostListener('document:ionBackButton', ['$event'])
private overrideHardwareBackAction($event: any) {
    $event.detail.register(100, async () => {
          // Do what you want
    });
}
peterpeterparker commented 6 years ago

@RHinderiks I've updated my previous answer, furthermore to the listener, the $event.detail.register is important

bergben commented 6 years ago

How about in v4-beta.13 which includes https://github.com/ionic-team/ionic/commit/6a5aec8 ?

mateusduraes commented 6 years ago

There is a way to register a custom function to hardware back button and stop the default event / propagation? I have the following scenario, i have a modal that i need to call a function and dismiss with some data, when i touch the hardware back button, the function, obviously, are not being called. I need to call this function but i need also to prevent the default event.


private handleHardwareBackButton(): void {
    const sub = this.platform.backButton.subscribe((event: Event) => {
      event.stopImmediatePropagation();
      event.stopPropagation();
      event.preventDefault();
      this.dismiss();
    });
    this.subscriptions.push(sub);
  }

  ngOnInit() {
    this.handleHardwareBackButton();
  }

  ngOnDestroy() {
    unsubscribeSubscriptions(this.subscriptions);
  }

The function is called, but the event is not stopped.

peterpeterparker commented 6 years ago

you could use subscribeWithPriority see above commit linked with this issue release in beta.13

peterpeterparker commented 6 years ago

or HostListener as described in my example above

mateusduraes commented 6 years ago

@peterpeterparker Thanks, the following code works for me.

 private handleHardwareBackButton(): void {
    const sub = this.platform.backButton.subscribeWithPriority(9999, () => {
      console.log('called');
      this.dismiss();
    });
    this.subscriptions.push(sub);
  }

  ngOnInit() {
    this.getStates(this.payload);
    this.handleHardwareBackButton();
  }

  ngOnDestroy() {
    unsubscribeSubscriptions(this.subscriptions);
  }

I used 9999 as the priority because it doesn't work with 1. I don't know witch is the highest priority inside the framework.

StarDee commented 6 years ago

@RHinderiks here you go for v4-beta.12, credit @manucorporat too:

@HostListener('document:ionBackButton', ['$event'])
private overrideHardwareBackAction($event: any) {
    $event.detail.register(100, async () => {
          // Do what you want
    });
}

It's useful in the App,but what should I use on the web ?

RHinderiks commented 5 years ago

Is there also a way to override the function on a regular ion-back-button inside a ion-toolbar ? Right now you can give it a href but i would like to show a confirmation dialog first

ionitron-bot[bot] commented 5 years ago

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.