All the below testing and coding is done for Android phone.
I have created a new tabs project using ionic tabs. And I have 4 tabs at the bottom right now. Now the problem is I clicked on another tab no the view and then try to use the default SwipeToGoBack gesture in the phone and that does nothing.
If I go into any other page then the back gesture works fine, but when I am in the 4 pages of the tabs, it does not work.
I tried below options:
<ion-router-outlet id="main" [swipeGesture]="true"></ion-router-outlet>
IonicModule.forRoot({
swipeBackEnabled: true
}
But those did not help. I can even close the side menu bar opened by swipe back. But can not go to the previous tab by swiping back on phone. Even not able to close the app.
If the application is opened and no other tabs were clicked and I do the swipe back gesture then the app closes, but as soon as I click on other tabs then I can not even come out of the application.
I also tried adding back button listeners from the platform but that also did not help. I added below code part in the tabs.page.ts:
this.platform.backButton.subscribe(() => {
navigator.app.exitApp();
});
Any help on how to go back to the previous tab on swipe back gesture on phone and finally close the app if no previous tab history?
Links already tried:
https://github.com/ionic-team/ionic/issues/12927
Handling hardware back button in Ionic3 Vs Ionic4
Swipe through segment tabs - Ionic 3
export class TabsPage {
navigationProccess:Array<any> = [];
lastTabName:string = "";
currentBack:string = "";
constructor(private platform: Platform,private router:Router,private navctrl:NavController) {
this.router.events.subscribe((event:RouterEvent)=>{
if(event.url !== undefined){
if(this.lastTabName !== event.url && event.url !== this.currentBack){
// we put last tab name not equal event.url so the event don't go twice through array
// we put event.url not equal current back that is since when navcontroll in back button go back its considered a router event and we don't need it to be inserted again
this.pushTabHistory(event.url);
}
this.lastTabName = event.url;
}
});
this.platform.backButton.subscribeWithPriority(99999999,async()=>{
let pushHistoryCount = this.navigationProccess.length;
if(this.router.url.includes('tabs') == true && pushHistoryCount > 1){
let url = this.navigationProccess[pushHistoryCount-2].url;
this.navigationProccess.splice(pushHistoryCount-1, 1);
this.currentBack = url;
//currentBack should be assigned before navgiate back
this.navctrl.navigateBack(url);
}
})
}
pushTabHistory(tabName:string){
let navHistory = {
url:tabName
};
this.navigationProccess.push(navHistory)
}
}
Mate i've edited my answer and you where right.
Tabs don't have routeroutlet.cangoBack() since tabs and tabs/tab1ortab2ortab3 are considered one level and can't go backward.
Here i created the way to make navigation history inside array and go back and forward from this array but subscribe back is from tabs page and i make it like that since you could making it just for testing.
But As an advanced way here we go ->
1) Create A service tabnav:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class TabnavService {
public navigationProccess:Array<any> = [];
public lastTabName:string = "";
public currentBack:string = "";
constructor() { }
pushTabHistory(tabName:string){
let navHistory = {
url:tabName
};
this.navigationProccess.push(navHistory)
}
}
2)Inside Your TabsPage:
import { TabnavService } from './../services/tabnav.service';
import { Router, RouterEvent } from '#angular/router';
import { Component } from '#angular/core';
import { NavController, Platform } from '#ionic/angular';
#Component({
selector: 'app-tabs',
templateUrl: 'tabs.page.html',
styleUrls: ['tabs.page.scss']
})
export class TabsPage {
constructor(private platform: Platform,
private router:Router,
private tabNavService:TabnavService) {
if(this.platform.is('android')){
this.router.events.subscribe((event:RouterEvent)=>{
if(event.url !== undefined){
if(this.tabNavService.lastTabName !== event.url && event.url !== this.tabNavService.currentBack){
// we put last tab name not equal event.url so the event don't go twice through array
// we put event.url not equal current back that is since when navcontroll in back button go back its considered a router event and we don't need it to be inserted again
this.tabNavService.pushTabHistory(event.url);
}
this.tabNavService.lastTabName = event.url;
}
});
}
}
}
3) In app.component.ts :
import { TabnavService } from './services/tabnav.service';
import { Component, ViewChild } from '#angular/core';
import { Platform, IonRouterOutlet, NavController } 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 {
#ViewChild(IonRouterOutlet,{static:false}) routerOutlet:IonRouterOutlet;
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private router:Router,
private navctrl:NavController,
private tabNavService:TabnavService
) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
this.platform.backButton.subscribeWithPriority(99999999,async()=>{
let pushHistoryCount = this.tabNavService.navigationProccess.length;
if(this.router.url.includes('tabs') == true && pushHistoryCount > 1){
let url = this.tabNavService.navigationProccess[pushHistoryCount-2].url;
this.tabNavService.navigationProccess.splice(pushHistoryCount-1, 1);
this.tabNavService.currentBack = url;
//currentBack should be assigned before navgiate back
this.navctrl.navigateBack(url);
}else if(this.router.url.includes('tabs') == true && pushHistoryCount <2){
// here is the array less than 2 which is one (you could make it ==0 but i make it if app glitches or something)
//so if app is on main start point it exit on back pressed
navigator['app'].exitApp();
}
});
});
}
}
And Thats All ^^ . Any Help Just comment here again ^^.
Related
I am currently transitioning from Wix RNN V1 to V2, and so far I've managed to find the appropriate replacement APIs, except for overriding the back button on Android.
In V1 we could pass the overrideBackPress: true attribute, and then handle back button presses manually on the cooresponding screen.
However, in V2 I've found no such replacement, and the only topics I could find were this thread:
https://github.com/wix/react-native-navigation/issues/4217
I've implemented the suggestions there, but Wix navigation is still automatically closing screens even though it should be overwritten.
Any known a solution for this?
I had the same issue and the only way i could override the backpress behavior on both platforms is to replace the left back button with custom button and use the BackHandler of react native for the hardware button in Android. The code is as below.
Component A
//Navigate to component B from A
Navigation.push(this.props.componentId, {
component: {
name: 'ComponentB',
options: {
topBar: {
leftButtons: [{
id: 'backPress',
text: 'Back',
icon: require('backbutton.png')
}]
},
}
}
});
Component B
import React, { PureComponent } from 'react';
import { View, BackHandler } from 'react-native';
import { Navigation } from 'react-native-navigation';
export default class ComponentB extends PureComponent {
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
}
navigationButtonPressed({ buttonId }) {
switch (buttonId) {
case 'backPress': {
this.handleBackPress();
break;
}
}
}
handleBackPress = () => {
//Custom logic
//Go back if required
Navigation.pop(this.props.componentId)
//Stop the default navigation
return true;
};
//Render component
render() {
return (<View></View>);
}
}
You can use registerScreenPoppedListener:
Navigation.events().registerScreenPoppedListener((event) => {
if (event.componentId === "my-screen-id") {
// do something
}
});
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, "");
});
}
}
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 building an app that does an auto login for the user when app starts if it was closed without logging out the last time. This is done by setting a local storage value at successful login and reading it in the constructor of the landing page, which is the login page.
I used ionic build android -release to build and app worked fine. But it takes about 10 sec to load. After some search, I found building the app with --prod can reduce the load time and it did reduce my app load time to 4 sec.
But now the app can't read local storage value at the start. No errors., but, it just returns the value as null. In other pages the app can read the local storage though. Looks like some needed components are not fully loaded, when the app reaches the landing page. But, by the time user logins manually those are loaded. How do I implement an auto login while reducing the load time?
import { Component } from '#angular/core';
import { NavController, MenuController, NavParams } from 'ionic-angular';
import { AuthService } from '../../providers/auth-service';
import { Global } from '../../providers/global';
import { HomePage } from '../home/home';
import { Storage } from '#ionic/storage';
import { Network } from 'ionic-native';
#Component({
selector: 'page-login',
templateUrl: 'login.html'
})
export class LoginPage {
loginUserID: string = '';
loginUserPass: string = '';
showLogin = false;
constructor(private menu: MenuController, private navCtrl: NavController, private auth: AuthService, private global: Global, private storage: Storage, private navParams: NavParams) {
this.storage.get('user_id').then((value) => {
if(this.navParams.get('showLogin')){
this.showLogin = true;
}
else{
if(value && value != null){
this.navCtrl.setRoot(HomePage);
}
else{
this.showLogin = true;
}
}
});
if(this.global.hardwareBackAction != null){
this.global.hardwareBackAction();
}
}
ionViewDidLoad() {
this.menu.swipeEnable(false, 'side-menu');
}
public login() {
if (Network.type == 'none') {
this.global.showAlert('Information', 'No internet access');
}
else {
this.global.showLoading();
this.auth.login(this.loginUserID, this.loginUserPass).subscribe(allowed => {
this.global.loading.dismiss();
if (allowed) {
setTimeout(() => {
this.storage.set('user_id', this.loginUserID);
this.navCtrl.setRoot(HomePage);
});
}
},
error => {
this.showError(error);
});
}
}
showError(text) {
setTimeout(() => {
this.global.loading.dismiss();
});
this.global.showAlert('Error', text);
}
}
I have a couple of pages and I want to swipe left or right to navigate between them. For example my Tabs component is like :
import { Component } from '#angular/core';
import { HomePage } from '../home/home';
import { Search } from '../search/search';
import { Share } from '../share/share';
import { Notification } from '../notification/notification';
import { Profile } from '../profile/profile';
#Component({
selector: 'hn-tabs',
templateUrl: 'tabs.html'
})
export class TabsPage {
// this tells the tabs component which Pages
// should be each tab's root Page
home: any = HomePage;
search: any = Search;
share: any = Share;
notification: any = Notification;
profile: any = Profile;
constructor() { }
}
and I want to navigate to Search page from Home Page with a SWIPE - LEFT .
Is this possible ?
Please help !!