Search code examples
durandal

Durandal 2.0 routing parameterized routes (same route different parameter)


I am probably missing something basic, but when building navigation I am attempting to define multiple parameterized routes in the shell. The idea is that all of these routes will pass the user through to the same view/vm, but the parameter will determine content that is displayed after an ajax call). The routing itself works well, but the title is always passed through from the first route in the list.

activate: function () {
    router.makeRelative({moduleId: 'viewmodels'}).map([
        {
            route: 'grid/:page',
            title: 'Title 1',
            moduleId: 'grid',
            nav: 3,
            hash: '#grid/param1'
        },
        {
            route: 'grid/:page',
            title: 'Title 2',
            moduleId: 'grid',
            nav: 2,
            hash: '#grid/param2'
        },
        {
            route: 'grid/:page',
            title: 'Title 3',
            moduleId: 'grid',
            nav: 4,
            hash: '#grid/param3'
        },
        {
            route: 'grid/:page',
            title: 'Title 4',
            moduleId: 'grid',
            nav: 5,
            hash: '#grid/param4'
        }
    ]).buildNavigationModel();
};

So, regardless of which of the generated links a user clicks on, the Title is always returned as 'Title 1'. Nav order does not matter. The first physical object in the list will supply the title for all links. If I hard code the links into shell.html and use a splat in the router I do not have this problem, however, hard coding the links is not feasible nor desirable for the app.

So, the question is, what am I doing wrong?


Solution

  • In the code above there's truly only one route 'grid/:page'. By defining a parameterized route the router maps everything the match grid/:page to the first route. See more about that in the router documentation http://durandaljs.com/documentation/Using-The-Router/.

    Instead of using the router.navigationModel() build your own small navigation model.

    Top level down approach:

    Step 1 Defining a grid route with an optional parameter (/:page).

    router
        .makeRelative({moduleId: 'viewmodels'})
        .map([
            {
                route: 'grid(/:page)',
                title: 'grid page',
                moduleId: 'grid',
                hash: '#grid'
            }
        ])
        .buildNavigationModel();
    

    Step 2 Navigation model

    define(['plugins/router', 'knockout', './navItem'], function( router, ko, NavItem ) {
    
        var ctor = function(){
            this.childRouter = router;
            this.param = ko.observable('');
    
            this.navigation = ko.observableArray([
               new NavItem({title: 'Title 1', param: 'param1'}),
               new NavItem({title: 'Title 2', param: 'param2'}),
               new NavItem({title: 'Title 3', param: 'param3'}),
               new NavItem({title: 'Title 4', param: 'param4'})
            ]);
        };
    
        ctor.prototype.activate = function(param){
            this.param('Param: ' + (param || 'no param!'));
        };
    
        return ctor;
    
    });
    

    Step 3 Navigation item model

    define(['plugins/router'], function( router ) {
    
        var ctor = function(options){
          this._options = options || {};
          this.init(this._options)
        };
    
        ctor.prototype.init = function(options){
            this.title = options.title;
            this.param = options.param;
            this.hash = '#extras/optional/' + this.param;
        };
    
        ctor.prototype.isActive = function(){
          return router.activeInstruction().fragment === this.hash.replace('#', '');
        };
    
        return ctor;
    });
    

    Step 4 Navigation view

    <div class="navbar">
          <div class="navbar-inner">
              <ul class="nav" data-bind="foreach: navigation">
                  <li data-bind="css: { active: isActive() }">
                      <a data-bind="attr: { href: hash }, html: title"></a>
                  </li>
              </ul>
              <div class="loader pull-right" data-bind="css: { active: childRouter.isNavigating }">
                  <i class="icon-spinner icon-2x icon-spin"></i>
              </div>
          </div>
          <div>
              <h3 data-bind="text: param"></h3>
          </div>
      </div>
    

    Live version can be seen at: http://dfiddle.github.io/dFiddle-2.0/#extras/optional. Feel free to fork and adjust to your liking.