Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switching application root (setRoot()) breaks after the second run #400

Open
codeaid opened this issue Apr 14, 2016 · 18 comments
Assignees
Labels
bug

Comments

@codeaid
Copy link

@codeaid codeaid commented Apr 14, 2016

Firstly, apologies for the long-winded explanation but I thought it makes sense to explain everything step by step to make it easier to replicate the issue.

Background

I am writing a website that requires users to log in to access protected content. As expected, the login page has a completely different layout to all the authenticated pages that contain elements like toolbar, sidebar, navigation, etc.

Due to the lack of documentation I've not been able to find a definitive guide on what's the preferred way to implement multiple layouts and have, as a result, settled on having to have different application roots that I switch depending on whether the user is authenticated or not (for the purpose of this example I'll call them Login and App).

Application flow

When the application is first launched the configure(aurelia: Aurelia) method in main.ts checks if the current user is authenticated and if not sets the root to Login in the result of which the user is presented with the login form.

export function configure(aurelia: Aurelia) {
    aurelia.start()
        .then(() => {
            let root = authService.hasIdentity() ? 'app' : 'login';
            aurelia.setRoot(root);
        });
}

When the user submits the form and successfully authenticates with the server, the application stores the authentication identity locally, sets the root to App and redirects user to the welcome page:

this.authService
    .login()
    .then(() => {
        this.aurelia.setRoot('app')
            .then(() => {
                this.router.navigate('/welcome');
            });
    });

After that the user can log out of the session, which clears the local identity, sets the root back to Login and redirects user to /:

this.authService
    .logout()
    .then(() => {
        this.aurelia.setRoot('login')
            .then(() => {
                this.router.navigate('/');
            });
    });

At this point, if the user wants to log back in, the expected behaviour is that they click on the Login button, the root gets changed to App and they get redirected to /welcome, yet that is not the case as the promise returned by setRoot() never resolves and as a result the user remains on the same page.

The user does, however, get redirected if he/she clicks on the Login button again right after the previous attempt and from that point on the login -> logout -> login -> logout -> ... flow works as expected - the roots change and users don't get "stuck" on the login page.

Test case repository

I've created a simple test application (based on the Typescript skeleton app) which you can find here:
https://github.com/codeaid/skeleton-navigation

To make it easier to understand what I've done there, the application consists of the following core components:

  • Authentication service - validates credentials with the server and allows storing and clearing authentication identities locally on the clients. The "validation" is actually only a sleep to simulate a request delay.
  • Authorization step - ensures that a local identity is present before navigating to pages that have { settings: { auth: true } } configured on their routes. If no identity is present, it changes the application root to login and redirects to the login page.
  • Router configuration service - invoked only once in the main.ts file to register all available routes and add the authorization step.
  • Login - The login root/page.
  • Page1 and Page2 - Two pages that require authentication identity to be present to access them.

The installation is the same as always:

cd ./skeleton-typescript
npm install
jspm install
gulp watch

Using the test application

To replicate the issue I've described above simply gulp watch the project, switch to your browser and navigate to /. You will be presented with the login page containing a Login button and two buttons allowing you to navigate to /page1 and page2.

Click the Login button after which your local identity will get set and you will be redirected to page1. After that happens, click on the Log out button and you will be redirected back to the login page.

At this point if you click on the Login button again nothing happens and you remain on the same page. If you click on the button again, though, you get redirected to /page1 and if you keep logging out and in it works as expected.

Potential culprit

I tried to debug the code to understand what is causing the issue but ended up in the CompositionEngine and eventually gave up as I'm not even sure what's happening in there.

I placed a tonne of console.info calls around the aurelia-templating.js and got the impression that for some reason the promise returned from waitForCompositionComplete() does not get resolved and as a result the swap() function doesn't get triggered.

Here's the code I'm talking about, which could be the culprit (trimmed to only show the important bits):

CompositionEngine.prototype._createControllerAndSwap = function _createControllerAndSwap(context) {
  function swap(controller) {
    // ...
  }

  return this.createController(context).then(function (controller) {
    if (context.compositionTransactionOwnershipToken) {
      return context.compositionTransactionOwnershipToken.waitForCompositionComplete().then(function () {
        // this does not seem to get resolved
        return swap(controller);
      });
    }

    // context.compositionTransactionOwnershipToken is not empty, hence this doesn't seem to fire
    return swap(controller);
  });
};

That's as much as I can tell, the rest is in your hands! :)

Hope that helps and yet again, sorry for the wall of text!

@codeaid codeaid changed the title Switching application root (`setRoot()`) breaks after the second run Switching application root (setRoot()) breaks after the second run Apr 14, 2016
@andrewflierman

This comment has been minimized.

Copy link

@andrewflierman andrewflierman commented Jun 30, 2016

Any development on this issue? We've a similar behavior within our application: http://stackoverflow.com/questions/38120388/aurelia-switching-between-app-roots-with-different-route-configurations/

@EisenbergEffect

This comment has been minimized.

Copy link
Member

@EisenbergEffect EisenbergEffect commented Jul 6, 2016

@awflierman Can you confirm that it also "gets stuck" for you?

@EisenbergEffect EisenbergEffect added the bug label Jul 6, 2016
@spapaseit

This comment has been minimized.

Copy link

@spapaseit spapaseit commented Jul 8, 2016

I, as the author of the StackOverflow question that @awflierman links to, can confirm that it does "get stuck" for me.

@andrewflierman

This comment has been minimized.

Copy link

@andrewflierman andrewflierman commented Jul 12, 2016

@EisenbergEffect Yes I can confirm

@EisenbergEffect

This comment has been minimized.

Copy link
Member

@EisenbergEffect EisenbergEffect commented Jul 12, 2016

Can anyone create a repro of this issue? I have an application that sets root multiple times and does not exhibit the problem. Something must be different.

@spapaseit

This comment has been minimized.

Copy link

@spapaseit spapaseit commented Jul 15, 2016

@EisenbergEffect, will do, sorry for the delay.

In your app with multiple roots, does every, or at least several of the roots have defined routes as well? Because that's when our problems arise. An app with two roots, with only one of them defining routes works flawlessly.

@EisenbergEffect

This comment has been minimized.

Copy link
Member

@EisenbergEffect EisenbergEffect commented Jul 15, 2016

Ah. That must be it. In the app I have only one of the two roots has routes. That's the key I need to investigate further. Thank you!

@EisenbergEffect EisenbergEffect self-assigned this Jul 15, 2016
@spapaseit

This comment has been minimized.

Copy link

@spapaseit spapaseit commented Jul 27, 2016

@EisenbergEffect, have you had any chance to investigate this any further?

@wk-plumbline

This comment has been minimized.

Copy link

@wk-plumbline wk-plumbline commented Aug 19, 2016

I can also confirm this behaviour. Splitting an application into two areas with different layout, based on logged-in status, has been non-trivial (and is a non-negotiable requirement in our case). Everything else has been super easy to accomplish though :)

@mgerwinn

This comment has been minimized.

Copy link

@mgerwinn mgerwinn commented Aug 20, 2016

We're facing the same issue here. Anyhow the quickfix for us was really simple and already described here #519
. Just switch the router configuration to the activate method of your app

@ignusk84

This comment has been minimized.

Copy link

@ignusk84 ignusk84 commented Aug 21, 2016

Any update on this? The quick fix method does not work in my case.

@khenderick

This comment has been minimized.

Copy link

@khenderick khenderick commented Sep 5, 2016

I also want to inform about progress on this one. Having two application roots with their own separate routing makes a lot of sense in real-life apps (e.g. login, registration, ... vs the plain application's pages).

The workaround mentioned by @suamikim in the linked ticket #519 works for now, but I'd rather use a correct solution.

khenderick added a commit to openmotics/frontend that referenced this issue Sep 8, 2016
@abeam-intricity

This comment has been minimized.

Copy link

@abeam-intricity abeam-intricity commented Oct 5, 2016

I'm facing this issue, as well. Any updates? Thanks!

@johot

This comment has been minimized.

@aditeeau

This comment has been minimized.

Copy link

@aditeeau aditeeau commented Nov 5, 2016

please give the convention of =>

@Alexander-Taran

This comment has been minimized.

Copy link
Member

@Alexander-Taran Alexander-Taran commented Mar 3, 2018

Stale since 2016

@EisenbergEffect

This comment has been minimized.

Copy link
Member

@EisenbergEffect EisenbergEffect commented Mar 3, 2018

Ping @davismj to investigate since this relates to routing.

@wzrdtales

This comment has been minimized.

Copy link

@wzrdtales wzrdtales commented Dec 23, 2018

As I wrote in #804 it is definitely coming from the router and that maybe there needs to be two different routers when changing the root element, to have a predictable behaviour.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.