Ionic version: (check one with "x")
(For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1)
[ ] 2.x
[ x] 3.x
[ ] 4.x
I'm submitting a ... (check one with "x")
[ x] bug report
[ ] feature request
Current behavior:
When using the hook "ionViewCanEnter" and returning a promise, the navigation can freeze sometimes when a back navigation is performed.
In that case, it is not possible to navigate back or forward.
I suppose that there is a timing problem when the promise takes too long?
This bug cannot be reproduced in every case.
Unfortunately it occurs often enough to be considered a blocker in our productive app.
By some debugging I could see the following:
When the naviagtion is stuck for the first time, the promise returned by method _transitionStart in NavControllerBase is never fulfilled. The call "transition.onFinish(resolve)" does not call the given resolve callback.
Expected behavior:
The navigation should work, no matter how long the promise returned by ionViewCanEnter needs to fulfill.
Steps to reproduce:
Related code:
See https://github.com/chhe88/IonViewCanEnteExample
The first page has an implementation of ionViewCanEnter which causes a delay of 5 secs.
Ionic info: (run ionic info from a terminal/cmd prompt and paste output below):
cli packages: (/usr/local/lib/node_modules)
@ionic/cli-utils : 1.12.0
ionic (Ionic CLI) : 3.12.0
global packages:
cordova (Cordova CLI) : 7.0.1
local packages:
@ionic/app-scripts : 2.1.4
Cordova Platforms : ios 4.4.0
Ionic Framework : ionic-angular 3.6.1
System:
ios-deploy : 1.9.2
ios-sim : 6.0.0
Node : v7.8.0
npm : 4.6.1
OS : macOS Sierra
Xcode : Xcode 9.0 Build version 9A235
Misc:
backend : legacy


Thanks in advance for your help! :)
Wow I have exactly the same issue, I didn't expect there to be anyone else with that problem. I can click back and forth a 100 times in my app without issues but sometimes after X times the navigation dies. I also use IonViewCanEnter hooks everywhere and it is totally possible the server takes a while to respond sometimes.
I tried adding logging to IonViewCanEnter but when the router died that code doesn't even seem to get called anymore, it's just dead.
An ugly workaround which may cause security issues:
ionViewCanEnter(): Promise<boolean> {
return Observable.of(true).do(() => {
// do your magic here and navigate explicitly to somewhere else (e.g. to your login page) if necessary
}).toPromise();
}
Hello! Thank you for opening an issue with us!
I had time to take a quick look at this earlier. I noticed that in the browser, if I hit the back button more than once, it will toss an exception. The first click tries to go back, the second click causes the root page to go NULL and toss an error. Not ideal.
I'll look at this later, but in the meantime, I will share the strategy I use in my own apps:
ionViewCanEnter that can be resolved synchronously and then return true or false. This is generally limited to checking a set of permissions or roles fetched on startup and/or login.Sorry that the linked example of an extended HTTP service was not for an Ionic app, but the ones I have written for Ionic apps were all for a former employer rather than my own project.
Another idea I have is to perhaps override the back button event controlling everything yourself so you can't get multiple clicks while waiting. I have not had time to try that out yet. I may have time later today.
Hopefully, that can get you started thinking about things you can try, though.
Thank you for using Ionic
Hello @kensodemann,
Thank you for getting back to us this fast, I really appreciate it!
Sorry I didn't had a look at your code, but do you redirect page in your guards or some functions called by the guards and reject the promise?
For example check if user is still authenticated, if not redirect to login page and reject guard.
Could this be?
@mburger81
Yes, this is exactly my intention.
In my app, every page that requires authentication extends from a class that implements the following method:
ionViewCanEnter(): Promise<boolean> {
return authenticate() // authenticate returns an Obervable<boolean>
.do(authenticated => {
if (!authenticated) {
// navigate to the login page
}
}).toPromise();
}
It works well in most cases, but sometimes it produces this bug, although no re-authentication is performed.
This was a breaking change in a NOT major releasr, there is also an issue from me which is documenting this.
You can not redirect page and reject the guard.
Probably they would never resolve this, in there opinion this is not the right way to use a router, guards and security.
Adding code such as this to next.ts seems to alleviate the issue you are having:
export class NextPage {
private navigating = false;
@ViewChild(Navbar) navBar: Navbar;
constructor(public navCtrl: NavController) { }
ionViewDidLoad() {
this.navBar.backButtonClick = (e: UIEvent) => {
if (!this.navigating) {
this.navigating = true;
this.navCtrl.pop();
}
}
}
}
Not sure if that will help the general flow of your app, but it will at least prevent over-clicking when the async bits are running long.
@mburger81 so the ionViewCanEnter no longer waits on a promise to either return true or false?
@kensodemann Is there a way that this could be handled in ionic framework itself. When using the built in back buttons there's no reason they should be able to be triggered twice. The same with screen pushes actually, as long as the push hasn't been resolved another push action should probably be discarded.
This seems like a bug that we need to fix.
Thanks,
Dan
Minor tweak to my workaround above after looking at the existing code more:
this.navBar.backButtonClick = (e: UIEvent) => {
if (!this.navigating) {
this.navigating = true;
e.preventDefault(); // added these two lines.
e.stopPropagation();
this.navCtrl.pop();
}
}
@kensodemann
Thanks for your workaround suggestion.
I already tried this in our productive app when I noticed the issue, but like you said, it just alleviates the problem.
This problem can also occur when you hit the back button just once.
I just got the notion that it is better to reproduce when you hit the button repeatedly.
Looking at the Ionic Framework code that handles this, so long as your async ionViewCanEnter() implementation returns a Promise that is eventually fulfilled in some way, this should work. The only time it would freeze is if your Promise is never fulfilled, in which case it isn't really frozen so much as just waiting.
So that leads me to these questions:
@chhe88 Not sure if my post doesn't matter with your bug put for clarification.
You can still use Promise, which works.
But you can not REJECT the promise and redirect to another page.
BTW I read in the forum from some moderators and also developers that Promise in feature would be deprecated, so perhaps it could be better to use boolean instead of Promise in guards. But the problem mentioned by me is the same. You can also not redirect in guards and return false, in this case the navigation would not be redirected.
Thanks for the issue! This issue is being closed due to inactivity. 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.
Thank you for using Ionic!