Naadof
Naadof

Reputation: 517

Aurelia - waiting for a promise to return in the router prerender step

TL;DR - How can I delay the ProcessResult() function in aurelia-router.js from executing because I'm still waiting on the result of a promise in my code? It results in the right module rendering, with the wrong address/href.

Example: if you're on base-module, then click on admin, the admin module will load, but the href remains www.mycompany.com/#/base-module instead of www.mycompany.com/#/admin, and this error can be seen in the console:

ERROR [app-router] Error: Expected router pipeline to return a navigation result, but got [{}] instead

Longer version:

I have a prerender step in my router that checks whether the user is enabled for a particular module before rendering the view.

In my PreRenderStep class I have a run function, which calls a mediator to get the permissions for a user, then checks whether the module the user clicked on is in their enabled modules list. The call to the mediator involves a promise.

The problem is that the run promise in the prerender step resolves before a promise within the run method finishes. Therefore, the view is eventually rendered (because the user is enabled) but the previous href remains in the address bar.

Router:

 configureRouter(config, router) {
        config.title = "Tramonex";
        config.addPipelineStep('authorize', AuthorizeStep);
        config.addPreRenderStep(PreRenderStep);
        config.map([
            {
                route: ['', 'log-in-out'],
                name: 'home',
                moduleId: 'modules/authentication/log-in-out'
            },
            {
                route: 'passwordReset',
                moduleId: 'modules/authentication/password-reset',
            },
            {route: 'app', moduleId: 'app', auth: true},
            {
                route: 'base-module',
                name: 'base-module',
                moduleId: 'modules/base-module',
                href: 'base-module',
                nav: true,
                auth: true
            },
            {
                route: 'test1',
                name: 'test1',
                moduleId: 'modules/test1/test1',
                href: 'test1',
                nav: true,
                auth: true,
                settings: {moduleAuthRequired: true}

            },
            {
                route: 'test2',
                name: 'test2',
                moduleId: 'modules/test2/test2',
                href: 'test2',
                nav: true,
                auth: true,
                settings: {moduleAuthRequired: true}
            },
            {
                route: 'admin',
                name: 'admin',
                moduleId: 'modules/admin/admin',
                href: 'admin',
                nav: true,
                auth: true,
                settings: {moduleAuthRequired: true}
            },
        ]);

        this.router = router;
    }
}

PreRenderStep:

@inject(Mediator, AuthenticationService)
class PreRenderStep {

    constructor(mediator, authenticationService) {
        this.mediator = mediator;
        this.authenticationService = authenticationService;
    }

    run(navigationInstruction, next) {
       
        if (navigationInstruction.getAllInstructions().some(i => i.config.settings.moduleAuthRequired)) {

            this.redirect = false;
            this.mediator.getPermissionsForUser()
                .then(user => {
                    userPerms = user.modules;
                    var isEnabled = userPerms.includes(navigationInstruction.config.name);
                    if (!isEnabled) {
                        this.redirect = true;
                    }
                })
                .then(() => {
                    return this.redirect next.cancel(navigationInstruction.router.navigateToRoute('base-module')) : next();
                });
        }
        else {
            return next();
        }
    }
}

When the user clicks on a module that requires an auth check, the code is hit and whilst we're waiting on the promise from the mediator.getPermissionsForUser() to return, this code in aurelia-router.js is hit (the lines with stars):

function processResult(instruction, result, instructionCount, router) {
  if (!(result && 'completed' in result && 'output' in result)) {
    result = result || {};
    **result.output = new Error('Expected router pipeline to return a navigation result, but got [' + JSON.stringify(result) + '] instead.');**
  }

  var finalResult = null;
  if (isNavigationCommand(result.output)) {
    result.output.navigate(router);
  } else {
    finalResult = result;

    if (!result.completed) {
      if (result.output instanceof Error) {
        logger.error(result.output);
      }

      **restorePreviousLocation(router);**
    }
  }

Upvotes: 4

Views: 973

Answers (1)

mgiesa
mgiesa

Reputation: 1013

You need to return the promise that you're creating in the run function.

return this.mediator.getPermissionsForUser()

Upvotes: 3

Related Questions