2011-10-13 72 views
35

我将我的视图和路由器分隔成需要的单独文件。然后我有一个main.js文件实例化路由器,并呈现我的默认视图。使用Backbone.js路由器浏览通过require.js模块化的视图

我的路由器将视图('查看/:id')和编辑('编辑/:id')作为路由。在main.js中,当我实例化路由器时,我可以硬编码router.navigate('View/1',true)并且导航工作正常。在我的视图文件中,当我点击编辑链接时,我想调用router.navigate('View /'+ id,true),但我不知道该怎么做。

我已经成功调用Backbone.history.navigate('View /'+ id,true),但我不觉得我应该依赖全局Backbone对象。

我尝试通过({路由器:appRouter})到我的意见,所以我可以使用this.options.router.navigate(),但是这不适合我。

如果你很好奇,这里是从我的应用程序一串代码:

路由器:

define(['./View', './Edit'], function (View, Edit) { 
    return Backbone.Router.extend({ 
     routes: { 
      'View/:id': 'view', 
      'Edit/:id': 'edit' 
     }, 

     view: function (id) { 
      var model = this.collection.get(id); 
      var view = new View({ model: model }); 
      view.render(); 
     }, 

     edit: function (id) { 
      var model = this.collection.get(id); 
      var edit = new Edit({ model: model }); 
      edit.render(); 
     } 
    }); 
}); 

查看:

define(function() { 
    return Backbone.View.extend({ 
     template: Handlebars.compile($('#template').html()), 

     events: { 
      'click .edit': 'edit' 
     }, 

     render: function() { 
      //Create and insert the cover letter view 
      $(this.el).html(this.template(this.model.toJSON())); 
      $('#View').html(this.el); 

      return this; 
     }, 

     edit: function() { 
      Backbone.history.navigate('Edit/' + this.model.id, true); 
     }, 
    }); 
}); 
+0

嗨,我正在做类似你的事情,打算在我的View模块中使用router.navigate()。你是如何最终实现这一目标的? Thx非常提前。 – chaonextdoor

+4

我结束了使用Backgone.history.navigate。由于我将视图分离为视图,路由器和模型的单个文件,并使用require.js加载它们。将路由器或事件对象传递给我的所有视图太麻烦了。全球似乎是最有意义的,因为骨干已经在全球命名空间。 – MrGrigg

+1

so backbone.history.navigate等于router.navigate? – chaonextdoor

回答

20

与几乎任何骨干问题一样,有很多方法可以解决这个问题。我走近它在我的当前项目的方式是把一切都放在一个全球性的自定义命名空间,并用它来绕过必需的引用:然后

var MyNamespace = {}; 

MyNamespace.init = function() { 
    MyNamespace.appView = new MyAppView(); 
    MyNamespace.router = new MyRouter(); 
    // etc 
} 

意见可以参考的必要MyNamespace.router。但它看起来像这样将无法工作/不与require.js鼓励,所以这里有一些其他的选项:

  • 永远不要叫路由器明确 - 相反,改变全局状态对象路由器收听。这实际上是我在当前项目中完成的工作 - 有关更多详细信息,请参阅this response

  • 将路由器连接到顶层视图(通常称为AppView),使其可以全局访问,并使用AppView.router.navigate()

  • 创建另一个模块,该模块提供了一个内部调用Backbone.history.navigate()的实用函数navigate。这与你正在做的事情没有什么不同,但它会使它稍微更加模块化,并且始终不让你使用全局引用。这也允许您更改内部实现。

+0

您是否在需求模块之外声明了MyNamespace? 这是接近使用全局骨干对象,只是用我自己的对象,对吧? – MrGrigg

+0

我没有使用require.js,所以它不适用于我的情况。我认为我列表中的最后一个项目可能是最接近require.js成语的。 – nrabinowitz

+0

是的,我自己调用导航的实用程序并不是一个坏主意。如果我这样做,我可能会使相同的实用程序提供我的路由器作为选项,也许这将解决整个事情。 – MrGrigg

8

你能做到这一点的老式方法with window.location.hash :)

window.location.hash = "Edit/1" 

如果您不需要明确的路线,这里有一个备用解决方案。当你的应用程序启动时创建的任何地方延伸主干活动

window.EventDispatcher = _.extend({}, Backbone.Events); 

然后在一个对象,你应用程式,您可以监听事件

EventDispatcher.bind("mymodel:edit", this.editHandler, this); 

而且还从任何地方发出事件,data以下任何PARAMS你想要一起发送

EventDispatcher.trigger("mymodel:edit", data); 
+0

我尝试使用window.location之前,我发现我可以使用全局Backbone对象,但它并没有实际触发我的事件,这可能是一个完全不同的问题。 – MrGrigg

+0

如果您只使用路由来触发事件(您不需要url可见地更改,或者用户使用后退/前进按钮),那么我今天下午想到的一些内容可能会有所帮助。您可以创建一个全球性的“EventDispatcher”,我将其添加到上面的答案中。 – kreek

+0

谢谢KreeK。我已经来回想要URL对于这个特定的应用程序很重要。我已经看到至少有一个类似于您的全局事件调度程序的其他示例。谢谢你的帮助。 – MrGrigg

32

如果其他人正在寻找像我这样的问题的解决方案,我发布了我最终做的事情。如果您使用样板backbone.js,那么您将在router.js中有initialize()函数。我修改了initialize()功能如下所示:

initialize = function() { 
    var app_router; 
    app_router = new AppRouter(); 

    // Extend the View class to include a navigation method goTo 
    Backbone.View.goTo = function (loc) { 
    app_router.navigate(loc, true); 
    }; 

    Backbone.history.start(); 
}; 

由于Backbone.js的的继承的特别味道,这让让我打电话给MyView.goTo(location);从内我的任何意见。

+9

我不得不使用'Backbone.View.prototype.goTo =' –

+0

你可以在视图中使用它,在成功回调中,或者你会怎么做?这里是我遇到的问题,如果你想看到我所指的代码... http://stackoverflow.com/questions/19472777/clear-localstorage-and-change-the-view-backbone – Lion789

+0

非常优雅的解决方案!非常感谢。我已经使用了Backbone.View.prototype来实现另一个功能,但是这并不是我想到的。 – digaomatias

5

对我来说,与GOTO功能的解决方案有轻微的变化

Backbone.View.prototype.goTo = function (loc) { 
     appRouter.navigate(loc, true); 
    }; 
3

我知道这个问题是旧的工作,但我想知道你为什么不为了得到路由器使用require.js :

define(['./PathToRouter', ], function (router) { 
    return Backbone.View.extend({ 
     template: Handlebars.compile($('#template').html()), 

     events: { 
      'click .edit': 'edit' 
     }, 

     render: function() { 
      //Create and insert the cover letter view 
      $(this.el).html(this.template(this.model.toJSON())); 
      $('#View').html(this.el); 

      return this; 
     }, 

     edit: function() { 
      router.navigate('Edit/' + this.model.id, true); 
     } 
    }); 
}); 
+0

我不知道为什么我没有想到这个。我做过的大部分事情都没有要求路线,所以我还没有重新讨论过这段时间。我很高兴你插话。 – MrGrigg

+2

在上面的示例中,./PathToRouter不是路由器的实例,而是类定义。骨干路由器唯一的静态方法是无法导航! – LarZuK

+0

@LarZuK我很远离这个问题,我甚至没有想到,但你完全正确。 – MrGrigg

2

这种方法怎么样?作为主干在所有4个组件中实现了模板模式,通过一些设计,您可以通过应用程序的路由器为每个视图提供一个简单的导航机制,而无需进行任何循环引用(这是我在其他类似帖子中看到的,但尽量避免它)。

路由器组件,而不是太大的不同从其他路由器的例子:

define('Router', ['backbone', ... ], 
     function (Backbone, ...) { 

      var MyRouter = Backbone.Router.extend({ 
       routes: { 
        'viewA': 'viewA', 
        'viewB': 'viewB' 
       }, 

       initialize: function() { 
        ... 
       }; 
      }, 
      viewA: function() { 
       ... 
      }, 

      viewB: function() { 
       ... 
      } 
}); 

return MyRouter; 
}); 

应用,创建路由器实例并触发传递这种情况下,第一个视图:

define('App', ['backbone', ... 
], function (Backbone, ...) { 

    function initialize() { 

     //route creation 
     if (!this.routes) 
      routes = new Router(this); 
     //backbone history start 
     Backbone.history.start(); 

     //ViewA navigation, bigbang 
     if (!this.viewA) 
      this.viewA = new ViewA({router: this.routes}); 
     this.viewA.render(); 
    } 

    return { 
     initialize: initialize 
    }; 
}); 

基本视角,基部构造定义所有视图和导航方法:

define('BaseView', ['jquery', 'underscore', 'backbone', ... 
], function ($, _, Backbone, ...) { 
    var BaseView; 

    BaseView = Backbone.View.extend({ 
     id: '...', 

     constructor: function (options) { 
      this.router = options.router; 
      this.model = options.model; 
      Backbone.View.prototype.constructor.call(this); 
     }, 
     initialize: function() { 
      this.template = _.template(tpl); 
     }, 

     events: { 

     }, 
     render: function() { 
      $(this.el).html(this.template()); 

      return this; 
     }, 
     //Provides transparent navigation between views throught the backbonejs 
     //route mechanism 
     navigate: function(pageId) 
     { 
      this.router.navigate(pageId, {trigger: true}); 
     } 
    }); 

    return BaseView; 
}); 

查看实例,每个视图从基地之一,而不是主干延伸,继承了基础的行为:

define('ViewA', ['jquery', 'underscore', 'backbone', 'BaseView' 
], function ($, _, Backbone, BaseView) { 
    var ViewA; 

    ViewA = BaseView.extend({ 
     id: '...', 

     constructor: function (options) { 
      this._super("constructor"); 
     }, 

     ... 
     foo: function() 
     { 
      ... 

      this.navigate("viewB"); 
     } 
    }); 

    return ViewA; 
}); 

这对我的作品,也可以在其他项目中重复使用。

1

对我来说,我向主应用程序中添加了一个对象,像这样;你可以说windows.app.router.navigate({'',trigger:true})。在你看来,你可以说windows.app.router.navigate({'',trigger:true})。不知道在这种情况下全球范围确定是否是好的做法,但它对我有用。

0

我有一个用于路由AMD模块的新解决方案。

RequireJS路由器https://github.com/erikringsmuth/requirejs-router

这需要延迟加载AMD模块的方式为您导航到每一页。使用Backbone路由器,您需要预先将所有视图作为依赖项。这会在首页加载时加载所有应用程序的Javascript。当您导航到每个路由时,RequireJS路由器会延迟加载模块。

示例main。js用于运行你的应用程序

define([], function() { 
    'use strict'; 

    // Configure require.js paths and shims 
    require.config({ 
    paths: { 
     'text': 'bower_components/requirejs-text/text', 
     'router': 'bower_components/requirejs-router/router' 
    } 
    }); 

    // Load the router and your layout 
    require(['router', 'js/layout/layoutView'], function(router, LayoutView) { 
    var layoutView = new LayoutView(); 
    // The layout's render method should draw the header, footer, and an empty main-content section 
    // then load the content section. 
    // render: function() { 
    // this.$el.html(this.template({model: this.model})); 
    // router.loadCurrentRoute(); 
    // } 

    // Configure the router 
    router 
     .registerRoutes({ 
     home: {path: '/', moduleId: 'home/homeView'}, 
     order: {path: '/order', moduleId: 'order/orderView'}, 
     notFound: {path: '*', moduleId: 'notFound/notFoundView'} 
     }) 
     .on('statechange', function() { 
     // Render the layout before loading the current route's module 
     layoutView.render.call(layoutView); 
     }) 
     .on('routeload', function(module, routeArguments) { 
     // Attach the content view to the layoutView's main-content section 
     layoutView.$('#main-content').replaceWith(new module(routeArguments).render().el); 
     }) 
     .init({ 
     // We're manually calling loadCurrentRoute() from layoutView.render() 
     loadCurrentRouteOnStateChange: false 
     }); 
); 
); 
define([], function() { 
    'use strict'; 

    // Configure require.js paths and shims 
    require.config({ 
    paths: { 
     'text': 'bower_components/requirejs-text/text', 
     'router': 'bower_components/requirejs-router/router' 
    } 
    }); 

    // Load the router and your layout 
    require(['router', 'js/layout/layoutView'], function(router, LayoutView) { 
    var layoutView = new LayoutView(); 
    // The layout's render method should draw the header, footer, and an empty main-content section 
    // then load the content section. 
    // render: function() { 
    // this.$el.html(this.template({model: this.model})); 
    // router.loadCurrentRoute(); 
    // } 

    // Configure the router 
    router 
     .registerRoutes({ 
     home: {path: '/', moduleId: 'home/homeView'}, 
     order: {path: '/order', moduleId: 'order/orderView'}, 
     notFound: {path: '*', moduleId: 'notFound/notFoundView'} 
     }) 
     .on('statechange', function() { 
     // Render the layout before loading the current route's module 
     layoutView.render.call(layoutView); 
     }) 
     .on('routeload', function(module, routeArguments) { 
     // Attach the content view to the layoutView's main-content section 
     layoutView.$('#main-content').replaceWith(new module(routeArguments).render().el); 
     }) 
     .init({ 
     // We're manually calling loadCurrentRoute() from layoutView.render() 
     loadCurrentRouteOnStateChange: false 
     }); 
); 
); 
相关问题