【问题标题】:Backbone.js router/views logicBackbone.js 路由器/视图逻辑
【发布时间】:2012-07-03 09:32:16
【问题描述】:

我正在编写我的第一个 Backbone.js 应用程序,但在找出最佳编程方式时遇到了一些麻烦。我有两个主要观点:

  1. 显示我所有模型的索引。
  2. 显示要编辑的特定模型。

但是#2 有许多不同的“模块”,比如我可以编辑“新闻”部分或“关于”部分等... 所有这些模块都在导航栏中。

当我显示视图#1(所有模型的索引)时,该导航栏被隐藏。它在视图 #2(特定模型)中可见,以便在不同模块之间导航。

我有这样的路线设置:

routes: {
    '', 'index',
    'communities': 'index',
    'communities/:id': 'main',
    'communities/:id/news', 'news',
    'communities/:id/about', 'about'
},

所以我的问题是,当调用“新闻”或“关于”操作时,我是否在每个方法中添加导航栏?这不是多余的吗?我将拥有 8-10 个不同的模块,每次添加导航栏似乎非常重复。有没有更好的办法? 我希望导航栏隐藏的唯一一次是在显示索引时。

【问题讨论】:

    标签: javascript backbone.js


    【解决方案1】:

    我在创建我的第一个有点复杂的 Backbone 应用程序时遇到了同样的问题。除了您对冗余代码的关注之外,我还担心绑定到我的导航栏的事件可能不会随着导航栏的更改而解除绑定。为了解决这个问题,我最终创建了一个视图层次结构,用一个 ma​​nager 视图管理整个导航栏,并为我想显示的每种导航菜单类型单独的视图,这些视图将传递给要呈现到页面的管理器视图。

    这是我的实现示例。

    在我们开始之前,这是我添加到 Backbone 的 View 原型中的 close 函数,它取消绑定事件并删除视图

    Backbone.View.prototype.close = function() {
        if(this.beforeClose) { this.beforeClose(); }
        this.remove();
        this.unbind();
    }
    

    首先,这是我的经理视图。它的render 函数关闭当前显示的任何菜单并将其替换为作为view 传递给它的菜单。虽然有点多余,但我创建了一个显式的 empty 函数,以使我的路由器代码更易于理解。

    var App.Views.SubNavBar = Backbone.View.extend({
    
        currentView: null,
    
        el: '#subnav-wrap',
    
        render: function(view) {
            if(this.currentView) { this.currentView.close(); }
            this.currentView = view;
            this.$el.html(view.el);
        },
    
        empty: function() {
            if(this.currentView) { this.currentView.close(); }
            this.currentView = null;
        }
    
    });
    

    其次,这是一个 base 视图,我的所有特定导航菜单视图都扩展了它。由于它们都将具有相同的 tagNameclassNameidinitializerender 函数,因此可以将重复降至最低

    var App.Views.SubNavBase = Backbone.View.extend({
    
        tagName: 'ul',
    
        className: 'nav nav-pills',
    
        id: 'subnav',
    
        template: _.template($('#tmpl-subnav').html(),
    
        initialize: function() {
            if(this.setLinks) { this.setLinks(); }
            this.render();
        },
    
        render: function() {
            this.$el.html(this.template({links:this.links}));
            return this;
        }
    
    });
    

    以下是特定导航菜单的视图示例。您可以看到我需要做的就是定义我希望出现在菜单中的链接。当我实例化这个视图时,SubNavBase 的函数将处理用所需的 HTML 填充视图。请注意,我还附加了一些事件到此视图。

    var App.Views.Projects.DisplayNav = App.Views.SubNavBase.extend({
    
        setLinks: function() {
            this.links = {
                'Edit Project': {
                    icon: 'edit',
                    class: 'menu-edit',
                    href: '#projects/'+this.model.get('id')+'/edit'
                },
                'Add Group': {
                    icon: 'plus',
                    class: 'menu-add-group',
                    href: '#projects/'+this.model.get('id')+'/groups/new'
                },
                'Delete Project': {
                    icon: 'trash',
                    class: 'menu-delete',
                    href: '#'
                }
            }
        },
    
        events: {
            'click a.menu-delete' : 'delete'
        },
    
        delete: function(e) {
            e.preventDefault();
            // here goes my code to delete a project model
        }
    
    });
    

    现在,这是我用来将上面的链接对象转换为<li> 元素列表的 underscore.js 模板。请注意,我使用<@ 而不是<% 作为我的模板,因为这是一个rails 应用程序并且rails 已经使用<%

    <script type="text/template" id="tmpl-subnav">
        <@ _.each(links,function(link, title) { @>
        <li>
            <a href="<@= link.href @>" class="<@= link.class @>">
                <i class="icon-<@= link.icon @>"></i>
                <@= link.title @>
            </a>
        </li>
        <@ }); @>
    </script>
    

    最后,总而言之,这是一个示例 Router 函数,它创建和呈现导航菜单。发生的步骤如下:

    • App.Views.Projects.DisplayNav 被传递一个模型,并用相应的 HTML 填充其 this.el,由 underscore.js 模板确定
    • App.SubNavBar 使用新的菜单视图调用其 render 函数
    • App.SubNavBar 检查导航栏中当前是否有另一个菜单;如果是这样,它会调用其视图的close() 函数
    • App.SubNavBar 最终将传递的视图的 HTML 附加到自身,维护对视图的引用以供以后使用

    我只包含了路由器代码的相关部分

    var App.Routers.Projects = Backbone.Router.extend({
        routes: {
            'projects/:id' : 'display'
        },
    
        display: function(id) {
            var p = projects.get(id);
            var subnav = new App.Views.Projects.DisplayNav({model:p})
            App.SubNavManager.render(subnav);  // App.SubNavManager is an instance of App.Views.SubNavBar
        }
    });
    

    所有这些的好处是我现在可以将事件附加到我的菜单特定视图,如果用户导航到不同的内容并且菜单发生更改,管理器视图将负责解除绑定。

    当然,您可以使用许多其他模式来处理导航菜单,但希望这对您有所帮助。

    【讨论】:

    • 老兄,很棒的答案。老实说,后来想了想,我意识到拥有视图层次结构是最好的选择。然后我看到了这个,很详细。再次感谢!!!!
    • @0xSina 没问题。在决定采用这种方法之前,我进行了几次尝试,我开始喜欢这种方法。我想,还不如节省别人的时间和麻烦。我还将“管理器”方法扩展到我的应用程序的其他部分,这些部分会定期重新加载新内容,例如主要内容div
    【解决方案2】:

    试试这个:

    routes: {
        '', 'index',
        'communities': 'index',
        'communities/:id': 'main',
        'communities/:id/:section': 'openSection'
    },
    openSection : function(id, section){
        if( section ){
            this.addNavigationBar();
        }
        switch( section ){
            case 'news' :
                 this.news();
                 break;
            case 'about' :
                 this.about();
                 break;
            default: 
                 this.main();
        }
    }
    

    如果你的 url 包含一个部分,你将添加导航栏,然后像你一样调用你的普通方法。

    【讨论】:

      猜你喜欢
      • 2013-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-10
      • 2014-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多