0

I am trying to initialize a child router to build sub navigation for the customer section of my application.

The url i am trying to configure is:

#customer/1/orders/1

I defined a route to get to the customer view in my shell.js

define(['plugins/router', 'durandal/app'], function (router, app) {
    return {
        router: router, 
        activate: function () {
            router.map([
                { route: 'customer/:id*splat', moduleId: 'customer/customer' }
            ]).buildNavigationModel();

            return router.activate();
        }
    };
});

I created a customer view that contains sub navigation for the customer section. The navigation will use the customer id that was in the route. This view doesnt really do anything except show customer sub-navigation. I created a child router in this view.

define(['plugins/router', 'knockout'], function (router, ko) {
    var childRouter = router.createChildRouter()
        .makeRelative({
            moduleId: 'customer',
            fromParent: true
        }).map([
            { route: 'orders/:orderId', moduleId: 'orders' }
        ]).buildNavigationModel();

    var activate = function(id) {

    };

    return {
        router: childRouter,
        activate: activate
    };
});

My problem is that I can't seem to get the routing to work when I have a parameter in my parent router. The customer view gets routed to but the orders view doesn't. I will end up having more sub views under the customer section.

Justin
  • 512
  • 1
  • 8
  • 19
  • @blazkovicz I ended up having to add a custom hash to each of my routes `hash: '#customer/' + id + '/orders'` where `id` is the parameter in my activate function. Not sure if this is the best or correct method. – Justin Dec 02 '13 at 14:57

2 Answers2

3

I managed to get this working just fine with my parameterized route. My childRouter is second level and I'm also using {pushState: true} so have no #(hashes) in my hashes :) so you'll need to add some if you're not using pushState. It looks like this:

Folder structure looks like this:

app
 |
 |--users
     |
     |--profile
     |    |
     |    |--activity.html
     |    |--activity.js
     |    |--articles.html
     |    |--articles.js
     |    |--tags.html
     |    |--tags.js
     |
     |--index.html
     |--indexjs
     |--profile.html
     |--profile.js

Splat route in the top level router:

{ route: 'users/:userId/:slug*details', title: 'User',      moduleId: 'users/profile',      nav: false }

profile.js looks like this:

define(['plugins/router', 'plugins/http', 'durandal/app', 'jquery', 'knockout', 'timeago', 'bootstrap'], function (router, http, app, $, ko) {
    var childRouter = router.createChildRouter();

    var userProfile = function(user) {
        $.extend(this, user);
    };

    var userProfileViewModel = function () {
        var self = this;
        self.userProfile = ko.observable();
        self.router = childRouter;
        self.activate = function () {
            var userId = router.activeInstruction().params[0];
            var slug = router.activeInstruction().params[1];
            var userRoute = '/users/' + userId + '/' + slug;
            self.router.reset();
            self.router
                .makeRelative({
                    moduleId: 'users/profile',
                    route: 'users/:userId/:slug'
                })
                .map([
                { route: '', moduleId: 'articles', title: 'articles', nav: false, hash: userRoute + '/articles' },
                { route: 'articles', moduleId: 'articles', title: 'articles', nav: true, hash: userRoute + '/articles' },
                { route: 'tags', moduleId: 'tags', title: 'tags', nav: true, hash: userRoute + '/tags' },
                { route: 'activity', moduleId: 'activity', title: 'activity', nav: true, hash: userRoute + '/activity' }
                ]).buildNavigationModel();
            return self.loadUserProfile(userId);
        };
        self.loadUserProfile = function(userId) {
            var url = '/api/users/profile/' + userId;
            return http.jsonp(url).then(function(response) {
                self.userProfile(new userProfile(response));
            });
        };
    };
    return userProfileViewModel;
});

Note also that I'm returning a constructor function here, not an object since in my case I don't want a singleton for this view.

halfer
  • 19,824
  • 17
  • 99
  • 186
RobertTheGrey
  • 8,645
  • 5
  • 32
  • 45
  • I am torn on this answer as it *works* but it seems to trigger the activate callback on the parent router as well. Could you confirm that whenever you are passing in a new Id it only calls activate on the child router and not on the parent? – PW Kad Sep 30 '14 at 06:15
0

I'd keep it simple and start with some static routes at the top level

router.map([
    { route: 'customer/:id', moduleId: 'customer/customer' },
    { route: 'customer/:id/orders/:id', moduleId: 'customer/orders' },
    { route: 'customer/:id/xxx/:id', moduleId: 'customer/xxx' }
]).buildNavigationModel();

In order to have full life cycle control for each customer (order, xxx, etc.) instance return a constructor function instead of a singleton.

define(['knockout'], function (ko) {

    var ctor = function(){
       ...
    };

    //this runs on every instance
    ctor.prototype.activate = function(id) {  // or function(orderId, xxxID)

    };

    return ctor;
});

More info singleton vs constructor: http://durandaljs.com/documentation/Creating-A-Module.html

Norbert
  • 2,741
  • 8
  • 56
  • 111
RainerAtSpirit
  • 3,723
  • 1
  • 17
  • 18