How to use dropdown menus for Durandal navigation?

I just started working with Durandal and all the parts fall into place and I use the Hot Towel template to speed up the process.

One thing that disgusts me is to create a more complex hierarchical navigation system than the buttongroup. Here I want to finish:

Abc
A1 B1 C1
A2 B2 C2

A, B and C are top-level menus that do not have routes to them - they are just placeholders. I will have views and viewmodels for A1, A2, B1, B2, C1 and C2, and these hash tags must be active.

My best idea right now is to add a parent menu to each route URL and have a code in nav.html that dynamically adds each node to the corresponding parent based on the parsing of the URL. To be fully dynamic, it will dynamically add both parent nodes and child nodes.

{ url: 'A_A1', moduleId: 'viewmodels/A_A1', name: 'A1', visible: true } 

I searched a lot for examples of hierarchical navigation with Durandal, but saw nothing. Is there a best practice for extending navigation functionality beyond a simple one-dimensional list? Can I ignore some functions in the router that I don’t see that will allow me to do this without embedding hierarchy information in the names of the names?

EDIT: I just answered correctly, although I was unhappy with the solution presented. When choosing a structure for abstract and separate logic, presentation, and control, it seems silly to combine these constructs again to provide more than a basic navigation shell. I shifted my efforts to development in angularjs, where such things become much more intuitive and can maintain separation. I hope that in the near future Durandal can move a little further, and I will definitely re-evaluate it for future projects.

+7
durandal hottowel
source share
2 answers

You can hard write them to your shell view, but if you don't want to do this, you can do this -

In your opinion, create a non-working anchor with / # that does nothing, with drop-down from routes, and another with drop-down from b routes.

  <div class="nav-collapse collapse main-nav"> <div class="btn-group"> <a class="btn" href="/#"> <span class="text">A Routes</span> </a> <button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button> <ul class="dropdown-menu pull-right"> <!-- ko foreach: aRoutes --> <li data-bind="css: { active: isActive }"> <a data-bind="attr: { href: hash }, html: name"></a> </li> <!-- /ko --> </ul> </div> <div class="btn-group"> <a class="btn" href="/#"> <span class="text">B Routes</span> </a> <button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button> <ul class="dropdown-menu pull-right"> <!-- ko foreach: bRoutes --> <li data-bind="css: { active: isActive }"> <a data-bind="attr: { href: hash }, html: name"></a> </li> <!-- /ko --> </ul> </div> </div> 

Make some calculated observables for routes in your shell view model

  var aRoutes = ko.computed(function () { return router.allRoutes().filter(function (r) { return r.settings.aroute; }); }); var bRoutes = ko.computed(function () { return router.allRoutes().filter(function (r) { return r.settings.broute; }); }); 

and in determining the route -

  { url: 'a1', moduleId: 'viewmodels/a1', name: 'A1', visible: false, settings: {aroute: true} }, 

This sets all your routes to false, and then gives them another aroute attribute that is set to true. Computed filters are limited only to routes set to true.

+6
source share

I developed a general solution by reading this post. With this solution, you can dynamically add any routes to the drop-down menu on the navigator.

In main.js, where I specify my routes, I added a function to map the submenu object to the configuration object of the main navigation route.

 function mapSubNav(parentRouteInfo) { var subroutes = []; var length = arguments.length; for (var i = 0; i < length; i++) { subroutes.push(arguments[i]); } parentRouteInfo.settings.subroutes = subroutes; } var page = router.mapNav('page', null, 'Page'), sub1 = router.mapRoute('page/sub1', null, 'Sub1'), sub2 = router.mapRoute('page/sub2', null, 'Sub2'); mapSubNav(nav, sub1, sub2); 

Explanation:

The mapNav router mapNav returns a route definition that looks like this:

 { url:'flickr', //you provided this name: 'Flickr', //derived moduleId: 'flickr', //derived caption: 'Flickr', //derived (uses to set the document title) settings: {}, //default, hash: '#/flickr', //calculated visible: true, //from calling mapNav instead of mapRoute isActive: ko.computed //only present on visible routes to track if they are active in the nav } 

The mapSubNav helper function mapSubNav put a list of links to the routes that you want to display in the drop-down menu in the settings object. In this example, the result would be:

 { url:'page', name: 'Page', moduleId: 'page', caption: 'Page', settings: { subroutes: [nav, sub1, sub2] }, hash: '#/page', visible: true, isActive: ko.computed } 

I expanded my viewmodel to look like this:

 define(function (require) { var router = require('durandal/plugins/router'); var app = require('durandal/app'); var ViewModel = function () { var self = this; self.router = router; self.dataToggle = function (route) { return !!route.settings.subroutes ? 'dropdown' : ''; }; self.html = function (route) { return !!route.settings.subroutes ? route.name + ' <b class="caret"></b>' : route.name; }; self.hash = function (route) { return !!route.settings.subroutes ? '#' : route.hash; }; self.divider = function (route, parent) { system.log('Adding', route, 'to dropdown', 'Parent', parent); return route.hash === parent.hash; }; self.activate = function () { return router.activate('welcome'); } }; return new ViewModel(); }); 

Note:

Additional functions in shell.js will determine which attributes should be added to the DOM elements in the navigator.


And finally, mine I edited the shell view to look like this:

 <div class="nav-collapse collapse"> <ul class="nav navbar-nav" data-bind="foreach: router.visibleRoutes()"> <li data-bind="css: { active: isActive, dropdown: !!settings.subroutes }"> <a data-bind="css: { 'dropdown-toggle': !!settings.subroutes }, attr: { href: $root.hash($data), 'data-toggle': $root.dataToggle($data) }, html: $root.html($data)"></a> <ul data-bind="foreach: settings.subroutes" class="dropdown-menu"> <li><a data-bind="attr: { href: hash }, html: name"></a></li> <li data-bind="css: { divider: $root.divider($data, $parent) }"></li> </ul> </li> </ul> </div> 

Result:

The end result is a menu item with a drop-down switch. The drop-down menu will contain a link to the parent and one link per subroutine. Something like that:

 --------------------------------------------- Menu items... | Page v | More menu items... --------------------------------------------- | Page | ---------- | Sub 1 | | Sub 2 | ---------- 

jQuery does not allow you to map a route to a drop-down menu button, so I made a parent route by storing the link to myself in the list of routines. There will be a link to the parent route on nav.

+3
source share

All Articles