Ionic-framework: Navigation trade off

Created on 18 Jul 2017  路  13Comments  路  Source: ionic-team/ionic-framework

Ionic version: (check one with "x")
[ ] 1.x (For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1)
[ ] 2.x
[x] 3.x

I'm submitting a ... (check one with "x")
[ ] bug report
[x] feature request
[ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/

Current behavior:
Having an app with navigation guards (ionViewCanEnter), and URL segments, does not allow a fallback for page evaluation.

Having an app with authentication, lets say I have 3 pages:

| page/link | guard |
|-----------|--------------------------|
| login | isGuest : Promise |
| home | isAuthenticated: Promise |
| other | isAuthenticated: Promise |

If I don't set rootPage, nothing happens by default, so I have to set it, and the only choice is login

Now if I go to /home, I see a blank page, because it tried to load the home page, but the user is not authenticated. User must go to either / or /login.

After the user is authenticated, now when he goes to /home, everything is fine, but if by accident he pressed on a link of /login, there is a blank page (can't enter, so nothing)

Finally, one would say: Check if the user is authenticated. if they are, set rootPage to home, and if not to login.

But what if the user (authenticated) goes to /other? they are immediately redirected to home. A possible suggestion would be to check the active page, but because of the nav guard, there is no active page before we know if the user is authenticated, so must also add a timeout for it to happen asyncronically.

Note: this does not happen only with authentication, even with sequential nav guards, like checking if page got all parameters.

Expected behavior:
I think there should be an option to bind a function to the nav, like onCantEnter or something, that fires only if the first page it tries to load can not be entered. It would get the component it tried to load, and let the user do whatever with that information, for example:

this.nav.onCantEnter((page) => {
  if(UNAUTHENTICATED_PAGES.indexOf(page.name) > -1)
    this.nav.setRoot('HomePage');
  else 
    this.nav.setRoot('LoginPage')
});

Note: I din't find where in the code it does that first load and executes ionViewCanEnter, so I can't create a PR.

Ionic info: (run ionic info from a terminal/cmd prompt and paste output below):

global packages:

    @ionic/cli-utils : 1.5.0
    Cordova CLI      : 7.0.1
    Ionic CLI        : 3.5.0

local packages:

    @ionic/app-scripts              : 2.0.2
    @ionic/cli-plugin-cordova       : 1.4.1
    @ionic/cli-plugin-ionic-angular : 1.3.2
    Cordova Platforms               : browser 4.1.0
    Ionic Framework                 : ionic-angular 3.5.2

System:

    Node       : v8.1.2
    OS         : Windows 10
    Xcode      : not installed
    ios-deploy : not installed
    ios-sim    : not installed
    npm        : 5.0.3 
stale issue

All 13 comments

@AmitMY
Curious if your issue is the same one we faced?

We wrap ion-nav in a

We do not set the rootPage until we verify if the user is logged in or not. If logged in we set the rootPage to their homepage and set hideHomePage = false. This means that the logic to render the page is not called until we know the state of the user.

Not sure what issue you faced, as you come on empty when I filter issues

I did the same idea, setting rootPage to null, and updating only when I know the state, but it does not work well with dynamic links, like in the example above /other.

It is quite similar to https://github.com/ionic-team/ionic/issues/11459

Is this a website or a mobile app?

My current app works as such. Are you trying to do something similar?

Logged in user
www.test.com/my-account/pay-bill -> If user token, log user in, go to page.
www.test.com -> If user token, log user in, go to dashboard which is /my-account

Non Logged in user
www.test.com/my-account/pay-bill -> If no user token, go to Generic Login. Once logged in go to pay-bill

www.test.com -> if no user token stay on page.

CSR view
If csr.test.com -> redirect user to csr page

Both.
So you check for the entering page, cool. What method are you using to get that info?

I have an auth class as follows
````
export abstract class Auth {

isAccountServiceReady: boolean = false;

constructor(public navCtrl: NavController, public navUtil: NavUtility, public loginService: LoginService, public userService: UserService, public _logger: Logger, public accountListService: AccountListService) {
this.accountListService.accountReadyEmitter.subscribe((user: User) => {
this.isAccountServiceReady = true;
});
}

ionViewCanEnter(): boolean | Promise {
if (!this.userService.isLoggedIn() && !this.userService.isLoggingIn) {
if (this.navUtil.moveToPage) {
this.loginService.genericLoginModalRedirectionName = this.navUtil.moveToPage;
} else {
this.loginService.genericLoginModalRedirectionName = this.loginService._location.path();
}
this.navUtil.navigate(this.navCtrl, 'GenericLoginPageComponent', true);
}
else
return true;
}

ionViewDidEnter() {
if (this.userService.user.currentAccount != undefined) {
this.isAccountServiceReady = true;
}
}

}

The interesting part here for me is NavUtility. Mind sharing that? I am interested to know how you find moveToPage

I have a whole lot of custom logic to manage navigation between pages atm.

I have a custom directive that allows navigation. As well as logic to use the serializer to get the component the current page is on. The I have the ability to setRoot and handle failures in that utility. At the moment it has error cases custom for our app that handles some odd cases due to the serializer. Let me see if I can get a slimmed down version for you.

I also have analytics tracking in the utility.

I guess what you care about is this line

      this.moveToPage = segments[0].name;
      return nav.setRoot(segments[0].name, Object.assign({}, segments[0].data, moveToPageParam), navOptions)
        .then(res => {
          this.moveToPage = null;
        })

If there is an issue navigating to a page we set the after generic login page as the last page we tried to navigate to. Else we set it to null so that we set the last page as the current page we are on per the browser.

Hi, this is exactly my opinion.
I and some others tried already to figure out this design patterns and we opened some issues and feature requests like #12193 and #11459

@ztecharoberts solutions is nice, and something we already tried or at least something similar. But there is something I'm not sure about:
On ionViewCanEnter you never return false when user is not logged in and you redirect to Page with this.navUtil.navigate. What is happening in this case?? Is the page accessing or not?
In our case the page was accessing and then redirect to new page. In this case it depends which is faster, if the redirect page is fast you don't see open the page, if not you firstly see the page and will then be redirected to new page. Perhaps you don't will see it because you are working if ngIf?

I think implementing a classical solution of Guards with the possibility to have multiple states and redirect user, would much more power, is much more readable code and more flexible.

In the past, redirecting roots on guards was available but removed on 3.5.x. So the rooter how is it know (working good) with the ability to redirect pages would be most of the things we all need.

The good news are, Dan wrote to work on it soon, I hope we can test and give feedback in a few days.

Thx to all

@mburger81

all of our pages show a loading spinner until accounts are ready.

        <div *ngIf='isAccountServiceReady==true'>
            <component [accountNumber]='accountNumber'></component>
        </div>
        <div *ngIf='isAccountServiceReady==false'>
            <loading></loading>
        </div>

Okay, but there are could be code in IonDidEnter and others, so there should also be no code in it. Right?

The idea is our pages contain little to no code in them. We have a lot of our logic based in components. If the component is not rendered none of our code is executed.

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!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Nick-The-Uncharted picture Nick-The-Uncharted  路  3Comments

SebastianGiro picture SebastianGiro  路  3Comments

RobFerguson picture RobFerguson  路  3Comments

alexbainbridge picture alexbainbridge  路  3Comments

BilelKrichen picture BilelKrichen  路  3Comments