如何使用Durandal导航的下拉菜单?

时间:2013-07-15 23:27:57

标签: knockout.js durandal hottowel

我刚刚开始使用Durandal并且所有部件都已落实到位,我正在使用Hot Towel模板来加快速度。

让我感到困惑的一件事是如何创建一个比buttongroup更复杂的分层导航系统。这就是我想要的结果:

A B C
  A1 B1 C1
  A2 B2 C2

A,B和C是没有附加路由的顶级菜单 - 它们只是占位符。我将为A1,A2,B1,B2,C1和C2提供视图和视图模型,并且需要将这些哈希标记作为活动链接。

我现在最好的想法是将父菜单附加到每个路径的网址中,并在nav.html中设置代码,根据解析网址将每个节点动态添加到相应的父网址中。要完全动态,它将动态添加父节点和子节点。

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

我已经用Durandal搜索了很多层次导航示例,但还没有看到任何东西。有没有最佳实践可以将导航功能扩展到简单的一维列表之外?我是否忽略了路由器中的一些功能,我没有看到,如果不将层次结构信息嵌入到视图名称中,我会这样做?

编辑:我只是将答案标记为正确,即使我对提出的任何解决方案都不满意。当选择一个框架来抽象和分离逻辑,表示和控制时,再次开始合并这些结构只是为了提供更多的基本导航shell似乎很愚蠢。我已经将我的开发工作转移到了angularjs,这样的事情变得更加直观并且可以保持分离。希望Durandal能够在不久的将来向前推进一步,我肯定会在未来的项目中对其进行重新评估。

2 个答案:

答案 0 :(得分:6)

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

在你的视图中,使用/#做一个不起作用的锚点,其中什么都不做,下拉a-routes,另一个下拉b路由。

        <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>

为shell视图模型中的路径创建一些计算的observable

    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;
        });
    });

并在您的路线定义中 -

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

这会将所有路由设置为false,然后为它们提供另一个设置为true的路由属性。计算过滤器仅限于将该设置设置为true的路由。

答案 1 :(得分:3)

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

在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);

说明:

路由器函数mapNav返回如下所示的路由定义:

{
    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
}

辅助函数mapSubNav将在设置对象的下拉菜单中放置要显示的路径的引用列表。在此示例中,结果将为:

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

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

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();
});

注意:

shell.js中的额外函数将决定应该在nav中添加到DOM元素的属性。


最后,我编辑了我的shell视图,看起来像这样;

<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>

结果:

最终结果是带有下拉切换的菜单项。下拉菜单将包含指向父级的链接以及每个链接到子路由的链接。像这样:

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

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