Knockout.js 如何使用下拉菜单进行Durandal导航?

Knockout.js 如何使用下拉菜单进行Durandal导航?,knockout.js,durandal,hottowel,Knockout.js,Durandal,Hottowel,我刚刚开始与Durandal合作,所有的部分都已就位,我正在使用热毛巾模板来加快进度 有一件事困扰着我,那就是如何创建一个比按钮组更复杂的分层导航系统。以下是我想要的结果: A B C A1 B1 C1 A2 B2 C2 A、 B和C是顶级菜单,它们没有附加任何路线-它们只是占位符。我将有A1、A2、B1、B2、C1和C2的视图和视图模型,并且需要这些散列标签作为活动链接 我现在最好的办法是将父菜单附加到每个路由的url中,并在nav.html中使用代码,根据对url的解析动态地将每个节点添加到

我刚刚开始与Durandal合作,所有的部分都已就位,我正在使用热毛巾模板来加快进度

有一件事困扰着我,那就是如何创建一个比按钮组更复杂的分层导航系统。以下是我想要的结果:

A B C
A1 B1 C1
A2 B2 C2

A、 B和C是顶级菜单,它们没有附加任何路线-它们只是占位符。我将有A1、A2、B1、B2、C1和C2的视图和视图模型,并且需要这些散列标签作为活动链接

我现在最好的办法是将父菜单附加到每个路由的url中,并在nav.html中使用代码,根据对url的解析动态地将每个节点添加到相应的父节点中。为了完全动态,它将动态添加父节点和子节点

        {
          url: 'A_A1',
          moduleId: 'viewmodels/A_A1',
          name: 'A1',
          visible: true
        }
我已经用Durandal搜索了很多分层导航示例,但还没有看到任何内容。除了简单的一维列表之外,是否还有扩展导航功能的最佳实践?我是否忽略了路由器中一些我没有看到的功能,这些功能可以让我在不将层次结构信息嵌入视图名称的情况下实现这一点


编辑:我只是将一个答案标记为正确,尽管我对给出的两种解决方案都不满意。当选择一个框架来抽象和分离逻辑、表示和控制时,重新开始合并这些结构似乎很愚蠢,仅仅是为了提供一个基本的导航shell。我已经将我的开发工作转移到angularjs上,这样的东西变得更加直观,并且可以保持分离。希望Durandal能在不久的将来取得更大的进展,我肯定会为未来的项目重新评估它。

您可以将它们硬编码到shell视图中,但如果您不想这样做,您可以这样做-

在您看来,使用/#创建一个不起作用的锚,该锚不起任何作用,下拉a-routes,另一个下拉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>
在路线定义中-

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

这会将所有路由设置为false,然后将aroute的另一个属性设置为true。计算出的过滤器仅过滤该设置为true的路由

读完这篇文章后,我想出了一个通用的解决方案。使用此解决方案,您可以将任何路线动态添加到导航中的下拉菜单中

在main.js中,我指定了我的路线,我添加了一个函数,用于将子菜单对象映射到主导航路线的设置对象

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);
辅助功能,
mapSubNav
,将在设置对象的下拉菜单中列出要显示的路线的参考列表。在本例中,结果将是:

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

我将shell viewmodel扩展为如下所示:

{
    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
}
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();
});
---------------------------------------------
 Menu items... | Page v | More menu items...
---------------------------------------------
               | Page   |
               ----------
               | Sub 1  |
               | Sub 2  |
               ----------

结果:

最终结果是一个带有下拉切换的菜单项。下拉菜单将包含一个指向父项的链接和一个指向每个子例程的链接。大概是这样的:

{
    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
}
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();
});
---------------------------------------------
 Menu items... | Page v | More menu items...
---------------------------------------------
               | Page   |
               ----------
               | Sub 1  |
               | Sub 2  |
               ----------

jQuery不允许将路由映射到下拉切换按钮,这就是为什么我让父路由在子例程列表中保留对自身的引用,在nav中仍然会存在到父路由的链接。

这是一个很好的主意-用父路由标记路由。我将对此进行一些研究,使其更具动态性,甚至可能作为Durandal小部件。希望它能帮助您,我已经使用此方法一段时间了,它工作得非常好,同时也是动态的。我只是看到我在b路线上没有正确的缩进,所以就修复了它。祝你好运。与我提供的答案有什么不同?@PWKad在我的解决方案中,一旦一切都设置好,你可以继续在main.js中使用
mapsubenav
,子菜单将自动添加,无需编辑任何其他文件。我不知道你的解决方案如何实现这一目标。我错过什么了吗?