The issue is that when clicking the back button in the phone/desktop browser the PWA will just close as there is no handling for back button by default for ionic3 PWA. I have searched everywhere for a solution that can handle back button for ionic3 PWA but I couldn't find one that currently work.
I found a solution here:
Android Back Button on a Progressive Web Application closes de App
But I didn't know how to fix it in my app as I tried to throw it in my code when my app initialize and now its disabling the back button completely so now I am seeking for help.
My Code in app.components.ts
initializeApp() {
this.platform.ready().then(() => {
// 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();
//Back button handling
window.addEventListener('load', function() {
window.history.pushState({}, '')
})
window.addEventListener('load', function() {
window.history.pushState({}, '')
})
window.addEventListener('popstate', function() {
window.history.pushState({}, '')
})
window.addEventListener('load', function() {
window.history.pushState({ noBackExitsApp: true }, '')
})
window.addEventListener('popstate', function(event) {
if (event.state && event.state.noBackExitsApp) {
window.history.pushState({ noBackExitsApp: true }, '')
}
})
});
}
Solution (Code in app.components.ts)
import { Platform, App, IonicApp, MenuController } from 'ionic-angular';
import { StatusBar } from '#ionic-native/status-bar';
import { SplashScreen } from '#ionic-native/splash-screen';
constructor(
platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen,
private app:App,
private ionicApp: IonicApp,
private menu: MenuController
) {
}
initializeApp() {
this.platform.ready().then(() => {
// 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();
this.setupBackButtonBehavior();
});
}
setupBackButtonBehavior () {
// If on web version (browser)
if (window.location.protocol !== "file:") {
// 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;
}
// Navigate back
if (this.app.getRootNav().canGoBack()) this.app.getRootNav().pop();
};
// Fake browser history on each view enter
this.app.viewDidEnter.subscribe((app) => {
history.pushState (null, null, "");
});
}
}
Related
I am using angular 7 and ionic 5. When i want to redirect to previous page using hardware back button, ionic app is closing itself. I am using below code but its not working.
this.platform.backButton.subscribeWithPriority(9999, () => {
document.addEventListener('backbutton', function (event) {
event.preventDefault();
event.stopPropagation();
console.log('hello');
}, false);
});
If you are using ionic with Capacitor you need to use this code in the constructor of app.component.ts:
import { App } from '#capacitor/app';
import { Location } from '#angular/common';
constructor(private _location: Location)
{
App.addListener('backButton', () =>
{
if (this._location.isCurrentPathEqualTo('/home'))
{
navigator['app'].exitApp();
}
else
{
this._location.back();
}
});
}
I use the directive for this
import { Directive, HostListener } from '#angular/core';
import { PlatformService } from 'src/app/services/platform';
#Directive({
selector: '[disableBackButton]'
})
export class DisableBackButtonDirective {
constructor(
private platformService: PlatformService
) { }
#HostListener('document:ionBackButton', ['$event'])
overrideHardwareBackAction(event: any) {
if (this.platformService.isPlatform('android')) {
event.detail.register(100, async () => {
event.stopImmediatePropagation();
event.stopPropagation();
event.preventDefault();
});
}
}
}
in page.html
<ion-content disableBackButton>
....
</ion-content>
in page.ts
this.platform.backButton.subscribe(() => {
console.log('hello');
});
Try this,
this.platform.backButton.subscribeWithPriority(10, () => {
this.router.navigate(['']); // route to previous page or component
});
I have developed a PWA (Tab based) using Ionic 3. It is working fine until hardware back button or browser's back button is pressed in android browser. If it is running from home screen, pressing hardware back will close app. If app is running in chrome in android (only tested in chrome), hardware back or browser's back will reload PWA's first page, not previously visited page. How to handle these events in Ionic 3 PWA?
I am using lazy load for all pages.
What I tried so far:
As per jgw96's comment here, I thought IonicPage will handle navigation itself. But it is not working.
Used platform.registerBackButtonAction, but it's not for PWA.
As per Webruster's suggestion below in Answers, tried code in app.component.ts. But no change.
Posting code:
import { Component, ViewChild } from '#angular/core';
import { Nav, Platform, AlertController, Alert, Events, App, IonicApp, MenuController } from 'ionic-angular';
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
rootPage:any = 'TabsPage';
constructor(public platform: Platform,
public alertCtrl: AlertController, public events: Events,
public menu: MenuController,
private _app: App,
private _ionicApp: IonicApp) {
platform.ready().then(() => {
this.configureBkBtnprocess ();
});
}
configureBkBtnprocess() {
if (window.location.protocol !== "file:") {
window.onpopstate = (evt) => {
if (this.menu.isOpen()) {
this.menu.close ();
return;
}
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._app.getRootNav().canGoBack())
this._app.getRootNav().pop();
};
this._app.viewDidEnter.subscribe((app) => {
history.pushState (null, null, "");
});
}
}
}
you have mentioned that you are working with the hardware back button on app and in browser so you didn't mention clearly what need to be done at what stage so i came up with the generalized solution which can be useful in most of the cases
app.component.ts
platform.ready().then(() => {
// your other plugins code...
this.configureBkBtnprocess ();
});
configureBkBtnprocess
private configureBkBtnprocess () {
// If you are on chrome (browser)
if (window.location.protocol !== "file:") {
// Register browser back button action and you can perform
// your own actions like as follows
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;
}
// Navigate back
if (this._app.getRootNav().canGoBack())
this._app.getRootNav().pop();
}
else{
// you are in the app
};
// Fake browser history on each view enter
this._app.viewDidEnter.subscribe((app) => {
history.pushState (null, null, "");
});
Solution 2
Try to Add the these event listener in the platform ready:
window.addEventListener('load', function() { window.history.pushState({}, '')
})
window.addEventListener('popstate', function() { window.history.pushState({},
'') })
I have pretty much same requirement but none of the solution completely works, so i came up with my own. here i have used an array to keep track of visited page and removes it on back click event.
Note: window.onpopstate gets called even on pushing new page
import { Platform, Nav } from "ionic-angular";
import { HomePage } from "../pages/home/home";
#Component({
templateUrl: "app.html"
})
export class MyApp {
rootPage: any;
#ViewChild(Nav) nav: Nav;
pageHistory: string[] = [];//to track page history
constructor(
platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen
) {
window.addEventListener("load", function() {
//adding a state to prevent app exit on back
window.history.pushState({ noBackExitsApp: true }, "");
});
platform.ready().then(() => {
window.onpopstate = evt => {
let view = this.nav.getActive();
if (this.pageHistory.find(x => x === view.name)) {
if (!view.name.startsWith("Home")) {//handle a condition where you want to go back
this.pageHistory = this.pageHistory.filter(n => n !== view.name);
this.nav.pop().catch(reason => {
console.log("Unable to pop :" + reason);
});
}
} else {
window.history.pushState({ noBackExitsApp: true }, "");
this.pageHistory.push(view.name);
}
};
this.rootPage = HomePage;
statusBar.styleDefault();
splashScreen.hide();
});
}
}
i am using React Native Share Method (https://facebook.github.io/react-native/docs/share.html) to share content on Android.
As per documentation, we can use it in following ways:
Share.share({
message: 'React Native | A framework for building native apps using React'
})
.then((result) => {
console.log(result.action) // returns sharedAction
})
.catch((error) => {
console.log(error)
})
So when we call Share method, we will get result in then and popup window is appeared which contains apps list with which we can share the message content.
Issue:
Popup window also contains a cancel button, so when user click on it, window get closed, but there is no method available/mentioned to capture it, in docs.
Is there any way to capture the cancel event, as i want to perform some action when user clicks on it.
Thanks in adv. :)
There is option in share dismissedAction but unfortunately this is IOS only . You can use react-native-share with prop "failOnCancel" to true and it will work on both android and ios
Sample Code
import React, { Component } from "react";
import { Button } from "react-native";
import Share from "react-native-share";
export default class ShareExample extends Component {
onShare = async () => {
const shareOptions = {
title: "Share file",
social: Share.Social.EMAIL,
failOnCancel: true
};
try {
const ShareResponse = await Share.open(shareOptions);
//setResult(JSON.stringify(ShareResponse, null, 2));
} catch (error) {
console.log("Error =>", error);
}
};
render() {
return <Button onPress={this.onShare} title="Share" />;
}
}
App Preview
This might help
import React, {Component} from 'react';
import {Share, Button} from 'react-native';
class ShareExample extends Component {
onShare = async () => {
try {
const result = await Share.share({
message:
'React Native | A framework for building native apps using React',
});
if (result.action === Share.sharedAction) {
if (result.activityType) {
// shared with activity type of result.activityType
} else {
// shared
}
} else if (result.action === Share.dismissedAction) {
// dismissed
}
} catch (error) {
alert(error.message);
}
};
render() {
return <Button onPress={this.onShare} title="Share" />;
}
}
I have basic Ionic application which I have disabled the back button on the app, is there a reason why the back button still works on an android device?
I am currently testing with ionic view.
here's my code:
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if(window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
});
$ionicPlatform.registerBackButtonAction(function(e) {
e.preventDefault();
}, 101);
})
According to ionic documentation
Your back button action will override each of the above actions
whose priority is less than the priority you provide.
And given that you want to completely disable the back button in all situations, and that the highest priority on actions in the referenced list is 500, you should provide a priority value more than 500, 600 for example. The code below should work when placed in $ionicPlatform.ready()
$ionicPlatform.registerBackButtonAction(function(e) {}, 600);
For anyone trying to sort this on Ionic 2:
http://www.codingandclimbing.co.uk/blog/ionic-2-android-back-button-13
and here's the actual post info:
In your app.ts, do the following to get the back button working as expected (mostly!):
initializeApp() {
this.platform.ready().then(() => {
this.registerBackButtonListener();
});
}
registerBackButtonListener() {
document.addEventListener('backbutton', () => {
var nav = this.getNav();
if (nav.canGoBack()) {
nav.pop();
}
else {
this.confirmExitApp(nav);
}
});
}
confirmExitApp(nav) {
let confirm = Alert.create({
title: 'Confirm Exit',
message: 'Really exit app?',
buttons: [
{
text: 'Cancel',
handler: () => {
console.log('Disagree clicked');
}
},
{
text: 'Exit',
handler: () => {
navigator.app.exitApp();
}
}
]
});
nav.present(confirm);
}
getNav() {
return this.app.getComponent('nav');
}
Note:
If you get errors about app not being a property of navigator:
1) Add a typings folder to your app root: e.g. app/typings
2) Add a file called: pluginshackyhacky.d.ts
3) Add for properties you need extended for TypeScript to compile.:
interface /*PhoneGapNavigator extends*/ Navigator {
app: any;
}
4) Add the pluginshackyhacky.d.ts to the compile in the tsconfig.json:
"files": [
"app/app.ts",
"app/typings/pluginshackyhacky.d.ts",
"app/typings/phonegap.d.ts"
]
You can see that I've also included the phonegap.d.ts file which includes a lot of missing properties/variables that allows TypeScript to compile without errors.
Hope this helps anyone having this problem.
Cheers.
Here is solution for Ionic 2:
constructor(
public platform: Platform, //Platform controller
public app: App, //App controller
) {
platform.ready().then(() => {
StatusBar.styleDefault();
Splashscreen.hide();
//Registration of push in Android and Windows Phone
platform.registerBackButtonAction(() => {
let nav = this.app.getActiveNav();
if (nav.canGoBack()){ //Can we go back?
nav.pop();
}else{
this.platform.exitApp(); //Exit from app
}
});
});
}
Change the priority from 101 to 100 to override the default hardware back functionality. If you had a priority of 100 already overriding the functionality, you could override that override with a priority of 101, if that makes sense.
$ionicPlatform.registerBackButtonAction(function(e) {
// android hardware back button was hit
}, 100);
Here is a list of all the priorities for the existing back button hooks
http://ionicframework.com/docs/api/service/$ionicPlatform/
I'm developing using IONIC framework. I'm having trouble with the hardware back button.
In android the hardware back button works perfectly, but the windows phone did not work out.
When I use the back button on windows phone minimizes the application and returns the device's home.
This function only works on android:
$ionicPlatform.registerBackButtonAction(function () {
console.log("Not work in WP");
}, 100);
help !!
I found solution.
In site
https://www.hoessl.eu/2014/12/on-using-the-ionic-framework-for-windows-phone-8-1-apps/
have a post calling -> Not fixed yet: Back Button
Not fixed yet: Back Button
With Windows Phone 8.0, listening on the “backbutton” event was pretty
simple, just as with android. On WP8.1, this event is not triggered
anymore. I haven’t figured out how to enable it yet. Any hint would be
appreciated.
But a user commented the solution. follows the passage that worked in
my case
Back Button Fix :
Set your $ionicPlatform.registerBackButtonAction
$ionicPlatform.registerBackButtonAction(function (evt) {
if (evt && evt.type == ‘backclick’) {
$ionicHistory.goBack();
return true;
}
}, 100);
Hookin WinJS and send it to Ionic :
if(ionic.Platform.isWindowsPhone)
{
WinJS.Application.onbackclick = function (evt) {
$ionicPlatform.hardwareBackButtonClick(evt);
return true;
}
}
Easy Fix, long time figuring it out
Example for placing the code inside app.js
angular.module('starter', ['ionic', 'starter.menu', 'starter.services'])
.run(function ($ionicPlatform, $ionicHistory, $state, ...) {
$ionicPlatform.registerBackButtonAction(function (evt) {
if (evt && evt.type == 'backclick') {
$ionicHistory.goBack();
return true;
}
}, 100);
...
$ionicPlatform.ready(function () {
...
if (ionic.Platform.isWindowsPhone()) {
WinJS.Application.onbackclick = function (evt) {
if ($state.current.name == 'app.home') {
//function responsible for exiting the application in Windows phone 8.1
cordova.exec(null, null, "ExitApp", "execute", []);
} else {
$ionicPlatform.hardwareBackButtonClick(evt);
return true;
}
}
}
}); ...
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'templates/login.html',
controller: 'LoginCtrl',
onEnter: function ($state, UserService) {
console.log("##### - " + UserService.get().isLogged);
if (UserService.get().isLogged) {
$state.go("app.home");
}
}
})
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html',
controller: 'MenuCtrl'
})
.state('app.secretary', {
url: '/secretary',
views: {
'menuContent': {
templateUrl: 'templates/secretary/menusecretary.html',
controller: 'MenuSecretaryCtrl'
}
}
})