Comment gérer l'action du bouton de retour sur Ionic 2?
Je veux pouvoir savoir quoi faire en fonction de la page affichée à l'utilisateur.
Je n’ai pas trouvé de bonne réponse à cette question, mais après un moment, j’ai trouvé un moyen de le faire moi-même. Je vais partager avec vous tous.
Merci
Voici comment je l'ai fait:
Dans chaque composant Page, j'ai créé une fonction appelée backButtonAction()
, qui exécute un code personnalisé pour chaque page.
Code:
import { Component } from '@angular/core';
import { Platform, NavController, ModalController } from 'ionic-angular';
import { DetailsModal } from './details';
@Component({
selector: 'page-appointments',
templateUrl: 'appointments.html'
})
export class AppointmentsPage {
modal: any;
constructor(private modalCtrl: ModalController, public navCtrl: NavController, public platform: Platform) {
// initialize your page here
}
backButtonAction(){
/* checks if modal is open */
if(this.modal && this.modal.index === 0) {
/* closes modal */
this.modal.dismiss();
} else {
/* exits the app, since this is the main/first tab */
this.platform.exitApp();
// this.navCtrl.setRoot(AnotherPage); <-- if you wanted to go to another page
}
}
openDetails(appointment){
this.modal = this.modalCtrl.create(DetailsModal, {appointment: appointment});
this.modal.present();
}
}
Et dans le app.component.ts
, j’ai utilisé la méthode platform.registerBackButtonAction
pour enregistrer un rappel qui sera appelé chaque fois que vous cliquez sur le bouton Précédent. À l'intérieur, je vérifie si la fonction backButtonAction
existe dans la page en cours et je l'appelle. Si elle n'existe pas, allez simplement dans l'onglet principal/premier.
Cela pourrait être simplifié s’ils n’avaient pas besoin d’effectuer des actions personnalisées pour chaque page. Vous pouvez simplement faire apparaître ou quitter l'application.
Je l'ai fait de cette façon parce que je devais vérifier si le modal était ouvert sur cette page en particulier.
Code:
platform.registerBackButtonAction(() => {
let nav = app.getActiveNav();
let activeView: ViewController = nav.getActive();
if(activeView != null){
if(nav.canGoBack()) {
nav.pop();
}else if (typeof activeView.instance.backButtonAction === 'function')
activeView.instance.backButtonAction();
else nav.parent.select(0); // goes to the first tab
}
});
si la page en cours est le premier onglet, l'application se ferme (comme défini dans la méthode backButtonAction
).
Ionic Dernière version 3.xx app.component.ts file
import { Platform, Nav, Config, ToastController} from 'ionic-angular';
constructor(public toastCtrl: ToastController,public platform: Platform) {
platform.ready().then(() => {
//back button handle
//Registration of Push in Android and Windows Phone
var lastTimeBackPress=0;
var timePeriodToExit=2000;
platform.registerBackButtonAction(() => {
// get current active page
let view = this.nav.getActive();
if(view.component.name=="TabsPage"){
//Double check to exit app
if(new Date().getTime() - lastTimeBackPress < timePeriodToExit){
this.platform.exitApp(); //Exit from app
}else{
let toast = this.toastCtrl.create({
message: 'Press back again to exit App?',
duration: 3000,
position: 'bottom'
});
toast.present();
lastTimeBackPress=new Date().getTime();
}
}else{
// go to previous page
this.nav.pop({});
}
});
});
}
J'ai utilisé les réponses ici et d'autres sources pour accomplir ce dont j'avais besoin ... J'ai remarqué que, lorsque vous générez l'application de production (--prod), cette approche ne fonctionne pas, car JS rend les choses plus simples et plus simples:
this.nav.getActive().name == 'PageOne'
A cause de cela, j'utilise ensuite dans l'instruction "if":
view.instance instanceof PageOne
Donc, le code final ressemble à ceci:
this.platform.ready().then(() => {
//Back button handling
var lastTimeBackPress = 0;
var timePeriodToExit = 2000;
this.platform.registerBackButtonAction(() => {
// get current active page
let view = this.nav.getActive();
if (view.instance instanceof PageOne) {
if (new Date().getTime() - lastTimeBackPress < timePeriodToExit) {
this.platform.exitApp(); //Exit from app
} else {
let toast = this.toastCtrl.create({
message: 'Tap Back again to close the application.',
duration: 2000,
position: 'bottom',
});
toast.present();
lastTimeBackPress = new Date().getTime();
}
} else if (view.instance instanceof PageTwo || view.instance instanceof PageThree) {
this.openPage(this.pages[0]);
} else {
this.nav.pop({}); // go to previous page
}
});
});
Selon la documentation Ionic 2 RC.4 de ici :
Vous pouvez utiliser la méthode registerBackButtonAction(callback, priority)
de l'API Platform
pour enregistrer l'action en appuyant sur le bouton Précédent.
L’événement du bouton Précédent est déclenché lorsque l’utilisateur appuie sur le bouton Précédent de la plateforme native, également appelé bouton «matériel». Cet événement est utilisé uniquement dans les applications Cordova exécutées sur les plates-formes Android et Windows. Cet événement n'est pas déclenché sur iOS car iOS ne comporte pas de bouton de retour matériel, comme le fait un appareil Android ou Windows.
L'enregistrement d'une action de bouton de retour matériel et la définition d'une priorité permettent aux applications de contrôler l'action à appeler lorsque le bouton de retour matériel est enfoncé. Cette méthode détermine quelle action du bouton Précédent enregistrée a la priorité la plus élevée et doit être appelée.
Paramètres :
Retourne: Fonction: fonction qui, lorsqu'elle est appelée, annulera l'enregistrement de l'action du bouton Précédent.
Dans Ionic 3 Lazy Loading, je n’ai jamais ressenti le besoin de gérer le comportement du navigateur lorsque, comme pour platform.is ("cordova"), j’ai créé la méthode suivante qui gère tous les scénarios antérieurs:
// If a view controller is loaded. Just dismiss it.
let nav = this.app.getActiveNav();
let activePortal = this._ionicApp._loadingPortal.getActive() ||
this._ionicApp._modalPortal.getActive() ||
this._ionicApp._toastPortal.getActive() ||
this._ionicApp._overlayPortal.getActive();
if(activePortal && activePortal.index === 0) {
/* closes modal */
activePortal.dismiss();
return;
}
// If a state is pushed: Pop it.
if (this.nav.canGoBack()) {
this.nav.pop();
return;
} else
// Else If its a tabs page:
if (this.nav.getActiveChildNav()) {
const tabs: Tabs = this.nav.getActiveChildNav();
const currentTab = tabs.getActiveChildNavs()[0];
// If any page is pushed inside the current tab: Pop it
if(currentTab.canGoBack()) {
currentTab.pop();
return;
}
else
// If home tab is not selected then select it.
if(tabs.getIndex(currentTab) !=0){
tabs.select(0);
return;
}
}
else
// If a menu is open: close it.
if (this.menu.isOpen()) {
this.menu.close();
return;
}
if (this.exitApp) {
this.platform.exitApp();
return;
}
this.exitApp = true;
const toast = this.toastCtrl.create({
message: this.exitMessage || 'press again to exit',
duration: 4000,
position: 'bottom',
cssClass: 'exit-toastr',
});
toast.present();
setTimeout(() => {
this.exitApp = false;
}, 2000);
En fait, ionViewWillLeave
fonctionne mieux dans mon cas.
Voici la documentation officielle sur la navigation dans le cycle de vie
J'ai trouvé le moyen le plus simple, ajoutez simplement le code suivant dans
app.component
:
this.platform.registerBackButtonAction((event) => {
let activePortal = this.ionicApp._loadingPortal.getActive() ||
this.ionicApp._modalPortal.getActive() ||
this.ionicApp._toastPortal.getActive() ||
this.ionicApp._overlayPortal.getActive();
if(activePortal && activePortal.index === 0) {
/* closes modal */
activePortal.dismiss();
} else {
if(this.nav.getActive().name == 'Homepage') { // your homepage
this.platform.exitApp();
}
else {
if(this.nav.canGoBack())
this.nav.pop();
this.nav.setRoot(Homepage);
}
}
},101);
C'est tout! Pas besoin d'ajouter de code supplémentaire sur chaque page!
J'ai une approche légèrement différente de celle de @amr abdulaziz. J'utilise le setTimeout pour contrôler en arrière ou quitter. J'espère que cela vous donnera une autre option pour implémenter le bouton de retour.
initBackButtonBehaviour() {
this.platform.registerBackButtonAction(() => {
console.log("Back button pressed");
if (this.readyToExit) {
this.platform.exitApp();
return;
}
let activePortal = this.ionicApp._loadingPortal.getActive() ||
this.ionicApp._modalPortal.getActive() ||
this.ionicApp._toastPortal.getActive() ||
this.ionicApp._overlayPortal.getActive();
if (activePortal) {
activePortal.dismiss();
activePortal.onDidDismiss(() => { });
return; // stop any further action after closing any pop up modal or overlay
}
if (this.menuCtrl.isOpen()) {
this.menuCtrl.close();
return; // stop any further action after menu closed
}
else if (this.nav.canGoBack()) {
this.nav.pop();
return; // stop any further action after navigation pop
}
else {
let activePage = this.nav.getActive().instance;
let whiteListPages = [HomePage];
// if current page is not in whitelistPage
// then back to home or login page first
if (whiteListPages.indexOf(activePage.constructor) < 0) {
this.nav.setRoot(HomePage);
return;
} else if (whiteListPages.indexOf(activePage.constructor) >= 0) {
this.utils.showToast('Press back button again to exit', 1500);
this.readyToExit = true;
setTimeout(() => {
this.readyToExit = false;
}, 1500);
} else {
console.error('cannot handle back button');
}
}
}, 101);
Meilleure solution pratique après une longue recherche.
cela fonctionne à 100%, et l'a testé dans un appareil réel
this.Platform.registerBackButtonAction(() => {
// try to dismiss any popup or modal
console.log("Back button action called");
let activePortal = this.ionicApp._loadingPortal.getActive() ||
this.ionicApp._modalPortal.getActive() ||
this.ionicApp._toastPortal.getActive() ||
this.ionicApp._overlayPortal.getActive();
if (activePortal) {
// ready = false;
activePortal.dismiss();
activePortal.onDidDismiss(() => { });
console.log("handled with portal");
return;
}
// try to close the menue
if(this.MenuController.isOpen()){
this.closeMenu();
return;
}
else if(this.nav.canGoBack()){
this.nav.pop();
return;
}else{
let activePage = this.nav.getActive().instance;
let whitelistPages = [LoginPage, HomePage];
// if current page is not in whitelistPage
// then back to home or login page first
if (whitelistPages.indexOf(activePage.constructor) < 0) {
this.nav.setRoot(this.userLoggedIn ? HomePage : LoginPage);
return;
}else if(whitelistPages.indexOf(activePage.constructor) > 0){
this.AppUtilities.showConfirm("Exit","Are you want to exist the app ? ").subscribe(
()=>{
this.Platform.exitApp();
},
()=>{}
)
}else{
console.error('cannot handel back button')
}
}
});
Enfin, j’ai trouvé une bonne solution pour la dernière version 3.xx ionique.
//Check Hardware Back Button Double Tap to Exit And Close Modal On Hardware Back
let lastTimeBackPress = 0;
let timePeriodToExit = 2000;
this.platform.registerBackButtonAction(() => {
let activePortal = this.ionicApp._loadingPortal.getActive() || // Close If Any Loader Active
this.ionicApp._modalPortal.getActive() || // Close If Any Modal Active
this.ionicApp._overlayPortal.getActive(); // Close If Any Overlay Active
if (activePortal) {
activePortal.dismiss();
}
else if(this.nav.canGoBack()){
this.nav.pop();
}else{
//Double check to exit app
if (new Date().getTime() - lastTimeBackPress < timePeriodToExit) {
this.platform.exitApp(); //Exit from app
} else {
this.toast.create("Press back button again to exit");
lastTimeBackPress = new Date().getTime();
}
}
});
vous pouvez essayer ces fonctions:
registerBackButton() {
this.platform.registerBackButtonAction(() => {
if (this.menu.isOpen()) {
console.log("Menu is open!", "loggedInMenu");
this.menu.close();
console.log("this.menu.isOpen(): " + JSON.stringify(this.menu.isOpen()));
return;
}
console.log("Checking for other pages");
let checkHomePage = true;
let max = Globals.navCtrls.length;
for (let i = 0; i < Globals.navCtrls.length; i++) {
let n = Globals.navCtrls[i];
if (n) {
if (n.canGoBack()) {
console.log("Breaking the loop i: " + JSON.stringify(i));
let navParams = n.getActive().getNavParams();
if (navParams) {
console.log("navParams exists");
let resolve = navParams.get("resolve");
if (resolve) {
n.pop().then(() => resolve({}));
} else {
n.pop();
}
} else {
n.pop();
}
checkHomePage = false;
return;
}
} else console.log("n was null!");
}
if (this.nav.getActive().instance instanceof TabsPage && !this.nav.canGoBack()) {
let popPageVal = this.backbuttonService.popPage();
console.log("popPageVal: " + JSON.stringify(popPageVal));
if (popPageVal >= 0) {
console.log("Switching the tab to: ", popPageVal);
this.switchTab(popPageVal);
} else {
console.log("Last page is HomePage");
if (this.alert) {
this.alert.dismiss();
this.alert = null;
} else {
this.showAlert();
}
}
} else {
console.log("Last page is not HomePage");
if (this.nav.canGoBack()) {
console.log("We can go back!");
this.nav.pop();
}
}
});
}
showAlert() {
this.alert = this.alertController.create({
title: "Exit?",
message: "Are you sure you want to exit?",
buttons: [
{
text: "Cancel",
role: "cancel",
handler: () => {
this.alert = null;
}
},
{
text: "Exit",
handler: () => {
this.platform.exitApp();
}
}
]
});
this.alert.present();
}
switchTab(tabIndex) {
if (Globals.tabs && tabIndex >= 0) {
console.log("Switch condition met");
Globals.tabIndex = tabIndex;
Globals.tabs.select(tabIndex);
Globals.tabs.selectedIndex = tabIndex;
}
}
J'espère que cela fonctionne avec vous.
J'ai pu accomplir cela dans le cas où nous sommes simplement en train de définir des pages racine.
import {Component, ViewChild, Injector} from '@angular/core';
import {Platform, MenuController, Nav, App, IonicApp, NavController} from 'ionic-angular';
import {StatusBar} from '@ionic-native/status-bar';
import {SplashScreen} from '@ionic-native/splash-screen';
import {InvitesPage} from "../pages/invites/invites";
import {RewardsPage} from "../pages/rewards/rewards";
import {ConnectionsPage} from "../pages/connections/connections";
import {MessagesPage} from "../pages/messages/messages";
import {ResourcesPage} from "../pages/resources/resources";
import {SignoutPage} from "../pages/signout/signout";
import {DashboardPage} from "../pages/dashboard/dashboard";
import {AccountPage} from "../pages/account/account";
import {HomePage} from "../pages/home/home";
import {TriviaPage} from "../pages/trivia/trivia";
import {Events} from "ionic-angular/util/events";
@Component({
templateUrl: 'app.html'
})
export class MyApp {
@ViewChild(Nav) nav: NavController;
// make HelloIonicPage the root (or first) page
public rootPage: any; //if logged in, go to dashboard.
public pages: Array<{title: string, component: any}>;
public user: any;
public routeHistory: Array<any>;
constructor(public platform: Platform,
public menu: MenuController,
public statusBar: StatusBar,
public splashScreen: SplashScreen,
private _app: App,
private _ionicApp: IonicApp,
private _menu: MenuController,
protected injector: Injector,
public _events: Events) {
this.initializeApp();
// set our app's pages
this.pages = [
{title: 'My Account', component: AccountPage},
{title: 'Dashboard', component: DashboardPage},
{title: 'Invites', component: InvitesPage},
{title: 'Rewards', component: RewardsPage},
{title: 'Connections', component: ConnectionsPage},
{title: 'Messages', component: MessagesPage},
{title: 'Resources', component: ResourcesPage},
{title: 'Trivia', component: TriviaPage},
{title: 'Sign Out', component: SignoutPage}
];
this.routeHistory = [];
this.user = {firstName: ''};
}
initializeApp() {
this.platform.ready().then(() => {
this._setupBrowserBackButtonBehavior();
let self = this;
if (sessionStorage.getItem('user')) {
this.user = JSON.parse(sessionStorage.getItem('user'));
self.rootPage = TriviaPage;
} else {
self.rootPage = HomePage;
}
this.routeHistory.Push(self.rootPage);
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.statusBar.styleDefault();
this.splashScreen.hide();
});
}
openPage(page) {
// close the menu when clicking a link from the menu
this.menu.close();
// navigate to the new page if it is not the current page
this.nav.setRoot(page.component);
//store route history
this.routeHistory.Push(page.component);
}
private _setupBrowserBackButtonBehavior() {
// Register browser back button action(s)
window.onpopstate = (evt) => {
// Close menu if open
if (this._menu.isOpen()) {
this._menu.close();
return;
}
// Close any active modals or overlays
let activePortal = this._ionicApp._loadingPortal.getActive() ||
this._ionicApp._modalPortal.getActive() ||
this._ionicApp._toastPortal.getActive() ||
this._ionicApp._overlayPortal.getActive();
if (activePortal) {
activePortal.dismiss();
return;
}
if (this.routeHistory.length > 1) {
this.routeHistory.pop();
this.nav.setRoot(this.routeHistory[this.routeHistory.length - 1]);
}
};
// Fake browser history on each view enter
this._app.viewDidEnter.subscribe((app) => {
if (this.routeHistory.length > 1) {
history.pushState(null, null, "");
}
});
}
}