【问题标题】:How to use dropdowns for Durandal navigation?如何使用下拉菜单进行 Durandal 导航?
【发布时间】:2013-07-13 23:44:04
【问题描述】:

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

让我难过的一件事是如何创建一个比按钮组更复杂的分层导航系统。这是我想要的结果:

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 在不久的将来能更进一步,我一定会为未来的项目重新评估它。

【问题讨论】:

    标签: knockout.js durandal hottowel


    【解决方案1】:

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

    在您看来,使用 /# 创建一个不工作的锚点,它什么都不做,下拉 a 路由,另一个下拉 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 视图模型中的路由创建一些计算的 observables

        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,然后将 aroute 的另一个属性设置为 true。计算出的过滤器只过滤设置为 true 的路由。

    【讨论】:

    • 这是一个绝妙的主意——用它的父级标记路由。我将对此进行一些改进,使其更具动态性,甚至可以作为 Durandal 小部件。
    • 希望对您有所帮助,我使用这种方法已经有一段时间了,它效果很好,同时也是动态的。我刚刚看到我在 b 路线上没有正确的缩进,所以就解决了这个问题。祝你好运。
    【解决方案2】:

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

    在我指定路由的 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 视图模型扩展为如下所示:

    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 中的额外功能将决定应该向导航中的 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 不允许您将路由映射到下拉切换按钮,这就是为什么我让父路由在子路由列表中保留对自身的引用在导航中仍然存在指向父路由的链接.

    【讨论】:

    • 和我提供的答案有什么不同?
    • @PWKad 使用我的解决方案,一切设置好后,您可以继续在 main.js 中使用mapSubNav,并且会自动添加子菜单,无需编辑任何其他文件。我没有看到您的解决方案如何实现这一目标。我错过了什么吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-03
    • 1970-01-01
    • 2016-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多