1

My Durandal application's startup logic in shell.js requires sending the user to one of two possible views depending on some conditional logic. Basically, if certain options have not previously been selected I'll send them to a Setup page, otherwise I'll send them to the normal start page.

Previously, in Durandal 1.x I would just pass a string indicating the starting module when calling activate. But in Durandal 2.x that has been deprecated.

So first, I'm wondering what is the recommended way to do this from the standpoint of the routes array? Should I just register both routes as if neither is the start module (like below) then conditionally add a another route to the array with the default route of ''?

{ route: "setup", moduleId: "setup", title: "Setup", nav: false },
{ route: "Students", moduleId: "students", title: "Students", nav: false }

The second part of my question involves how to handle the need to make a call to a web service as part of my conditional logic for determining which module is the start module. My startup logic involves checking the local storage of the browser, but sometimes I'll also need to make an ajax request to the server to get a bit of information.

My understanding is that router.activate() is a promise. Can I actually just create my own promise and resolve it by calling router.activate() after the ajax call has completed? Or is there another way I'll need to handle that? Sample code for how I was thinking I might handle this:

var deferred = $.Deferred();

//Setup and conditional logic here
var routes = setupRoutes();

$.get('/api/myStartupEndpoint')
 .done(function(results) {
    //check results and maybe alter routes
    deferred.resolve(router.map(routes).activate());
 });

return deferred.promise();

Does that make sense? I'm still converting my app over to Durandal 2.0.1 so I haven't been able to try this yet, but regardless of whether it does or not I want to find out what the recommended approach would be in this scenario.

Kevin Kuebler
  • 384
  • 1
  • 13

1 Answers1

2

The way I'd do it is this - actually I think it's similar to how you're already thinking, so I hope it makes sense:

  1. In your main.js, set your application root to the same module, no matter the circumstances. Since one of the main features of D2 is child routers, I'd suggest using the module name "root" for your application root, as it makes it easier to distinguish from "shell" modules (which I use for setting up child routers):

    app.start().then(function () {
        app.setRoot("root", "entrance");
    });
    
  2. In your root module, setup the routes as you have described:

    { route: "setup", moduleId: "setup", title: "Setup", nav: false },
    { route: "students", moduleId: "students", title: "Students", nav: false }
    
  3. When your root module activates, check if the user has been setup or not. Use the result of that check to work out if you want the user to be sent to the setup page, or the students page. Note however that you must activate your router before redirecting; you can use $.when to help you here:

    var checkUserSetup = function () {
        var deferred = $.Deferred();
    
        // check local storage, fire off ajax request, etc.
        // resolve the deferred once you know if the user is setup or not
        deferred.resolve(false);
    
        return deferred.promise();
    };
    
    root.activate = function() {
        mapRoutes();
    
        var deferred = $.Deferred();
    
        $.when(checkUserSetup(), router.activate()).done(function(isUserSetup) {
            if (isUserSetup) {
                router.navigate("students");
    
            } else {
                router.navigate("setup");
            }
    
            deferred.resolve();
        });
    
        return deferred.promise();
    }
    

Hopefully this also answers the second part of your question; but just in case - yes, you can return a promise from the activate method, and durandal will "block" until you've resolved that promise. However, note that the router's activate method also returns a promise - so your code won't quite work. You're resolving your deferred with another deferred; you'd need to do something more like this:

root.activate = function() {
    var deferred = $.Deferred();

    //Setup and conditional logic here
    var routes = setupRoutes();

    $.get('/api/myStartupEndpoint')
        .done(function(results) {
            //check results and maybe alter routes
            //don't resolve the deferred until the router has completed activation
            router.map(routes).activate().done(deferred.resolve);
        });

    return deferred.promise();
}

Hope that helps.

gerrod
  • 6,119
  • 6
  • 33
  • 45
  • Thanks for the explanation! I ended up using the second technique you showed (basically the same thing I was trying to do originally, except resolving the promise after the router activate promise is done). I haven't looked into the child router feature yet, and don't really see where I would need or want to do that in my app. So I decided to hold off on the 'root' module technique for now, at least until I understand the use case better. Thanks! – Kevin Kuebler Nov 15 '13 at 03:41
  • @gerrod I'm trying to do something very similar to this, but I think I'm missing something in the example you've posted. I'm getting 'Route Not Found' when calling `router.activate()` before `router.navigate("students")`, because there is no `''` route. Or should there be, and it isn't included here? If so what should it point to?! Happy to jump on a chat if you have a minute. Thanks – Shai May 20 '14 at 21:26
  • Hi @Shai - I'd need to see the example, but you usually want to either include a default route (''), or alternatively you can use `mapUnknownRoutes` to try and work out which route isn't mapping properly (see "Handling Unknown Routes", here - http://durandaljs.com/documentation/Using-The-Router.html). Hope that helps! – gerrod May 21 '14 at 01:05
  • Thanks. Yeah I got it working by adding a default route (''), which points to a viewmodel which just has `canActivate: function() { return false; }`. This then allowed me to conditionally run `router.navigate()` to the correct choice of start page. Cheers for your help... I'd got confused by the 'set up your routes as [...] setup [...] students' and didn't realise a default was needed! – Shai May 23 '14 at 11:29