Backbone.js - 触发路由之前/之后的调用方法

时间:2011-09-12 22:17:59

标签: backbone.js routes router

我希望在我的Backbone.js路由器中分别触发路由之前和之后调用setup / teardown方法。有人创造了一种优雅的方式吗?

13 个答案:

答案 0 :(得分:9)

_.wrap不是一个解决方案,如果您有20条路线,则必须将它们全部包裹起来。

但你可以用元编程

来做到这一点
class Backbone.FlexRouter extends Backbone.Router
  route: (route, name, handler) ->
    super route, name, ->
      @trigger "route:before"
      handler()
      @trigger "route:after"

UPD:我相信JS应该是这样的(但我没有测试过它)

var rp = Backbone.Router.prototype
rp.routeWithoutEvents = rp.route
rp.route = function(route, name, handler){
  var that = this
  this.routeWithoutEvents(route, name, function(){
    that.trigger("route:before")
    handler()
    that.trigger("route:after")
  })
}

答案 1 :(得分:8)

您考虑过_.wrap吗?

答案 2 :(得分:6)

这是一个简单的,覆盖Backbone.Router本身

(function () {
    _.extend(Backbone.Router.prototype, Backbone.Events, {        
        route: function (route, name, callback) {           
            if (!_.isRegExp(route)) route = this._routeToRegExp(route);
            if (!callback) callback = this[name];
            Backbone.history.route(route, _.bind(function (fragment) {               
                var args = this._extractParameters(route, fragment);                
                if (this.before && _.isFunction(this.before)) {
                    this.before(fragment);
                }                
                callback && callback.apply(this, args);
                this.trigger.apply(this, ['route:' + name].concat(args));
                if (this.after && _.isFunction(this.after)) {
                    this.after(fragment);
                }
                Backbone.history.trigger('route', this, name, args);
            }, this));
            return this;
        }
    });
}).call(this);

专注于行

if (this.before && _.isFunction(this.before)) {
    this.before(fragment);
}

if (this.after && _.isFunction(this.after)) {
    this.after(fragment);
}

您可以根据需要修改行

这是使用新的Backbone.Router类的客户端代码

var appRouter = Backbone.Router.extend({

routes: {},
before: function(){
   //your code here
   return true;
}

});

答案 3 :(得分:3)

Alexey的答案几乎是正确的,但有一些微妙的事情遗失了。

class ApplicationRouter extends Backbone.Router

  route: (route, name, callback = null) ->
    callback = @[name] if ! callback
    super route, name, ->
      @trigger 'route:before'
      result = callback && callback.apply(@, arguments)
      @trigger 'route:after'
      return result

答案 4 :(得分:1)

此插件可以满足您的需求。它适用于0.5.3。我不确定它是否适用于0.9.1。

https://github.com/angelo0000/backbone_filters

答案 5 :(得分:1)

我之前遇到过这个问题,我想我会分享我的解决方案,将“中间件”插入Backbone路由流程。目标是根据某些条件将用户重新路由到各种流,例如,功能标记,会话处理等。

Backbone.ProtectedRouter = Backbone.Router.extend({
  /*
  * Subclass of Router that monkeypatches route in order to protect certain
  * routes.
  *
  * If you want to add a protected route, add it to the protectedRoutes
  * object in this form:
  *   route: { method: fn, assertion: fn, args: [args..] }
  *
  * * method => the method to call if the assertion is true (the route should
  * be protected in the given scenario)
  *
  * * assertion => the function that decides whether or not the route
  * should be rendered
  *
  * * args => the arguments to be passed to method
  */
  route: function(route, name, handler) {
    var _this = this;
    Backbone.Router.prototype.route(route, name, function(){
      var boundHandler = _.bind(handler, _this),
          attrs, method, args, dfd;

      attrs = _.has(_this.protectedRoutes, route) ? _this.protectedRoutes[route] : null;

      if ( attrs && !attrs.assertion() ) {
        // In this scenario my flows all return Deferreds
        // you can make this event based as well.
        dfd = _this[attrs.method].apply(_this, attrs.args.concat([route]));
        dfd.then(boundHandler);
      } else
        boundHandler.apply(_this, arguments);
    });
  }
});

从那里你可以简单地用Backbone.ProtectedRouter哈希扩展protectedRoutes

var router = Backbone.ProtectedRouter.extend({
    protectedRoutes: {
      'home': { 
        assertion: function() { return is_logged_in; },
        method: 'renderLogin',
        args: ['some_arg']
      }
    },
    routes: {
      'home': 'renderHome'
    },
    ...
});

在这种情况下,如果针对home路由发出请求且is_logged_infalse,则会调用renderLogin方法并传递'some_arg'。在流之后,renderLogin将返回一个已解析的Deferred,导致调用原始处理程序(renderHome)。

我希望这会有所帮助。我也很乐意接受建议! :)

答案 6 :(得分:1)

我最近遇到了这种需求(检查用户是否经过身份验证)。不幸的是,Backbone没有给我们一个before / after事件,所以你需要覆盖或扩展Router.route。感觉不太干净,因为你必须从源代码复制并在那里编辑,但这是我找到的唯一方法。在Backbone默认代码(1.0.0)下面并标记我的自定义代码:

  Backbone.Router.prototype.route = function(route, name, callback) {
    if (!_.isRegExp(route)) route = this._routeToRegExp(route);
    if (_.isFunction(name)) {
      callback = name;
      name = '';
    }
    if (!callback) callback = this[name];
    // here my custom code
    callback = _.wrap(callback, _.bind(function(cb) {
      if (name == 'login' || sessionModel.authenticated()) {
        _.bind(cb, this)();
      } else {
        this.navigate('login', {trigger: true});
      }
    }, this));
    // finish my custom code
    var router = this;
    Backbone.history.route(route, function(fragment) {
      var args = router._extractParameters(route, fragment);
      callback && callback.apply(router, args);
      router.trigger.apply(router, ['route:' + name].concat(args));
      router.trigger('route', name, args);
      Backbone.history.trigger('route', router, name, args);
    });
    return this;
  };

注意_.wrap_.bind所以this是您在使用路由器时所期望的那个。否则我得到一个“这是未定义的”错误。

答案 7 :(得分:0)

ethnagnawl和Alexey都是正确的; _.wrap是正确的解决方案但是如果你有一堆路线并以正常的骨干方式写它们将会很痛苦。我意识到你可以这样做:

var Pages = {}
Pages.loginPage = function(){ ... }
Pages.mainPage = function(){ ... }

不是直接在Router.extend中定义路由处理程序,而是将它们加载到对象中,然后执行此操作:

_.map(Pages,function(func,name){                                                     
    Pages[name] = _.wrap(func,function(funky){                                       
        // Save original arguments                                                   
        var args = Array.prototype.slice.call(arguments,1);                          
        // Do stuff before the route                                                               
        funky(args);               
        // Do stuff after the route                                                  
    });                                                                              
});                                    

如果您需要以不同的方式处理它们的子集或其他内容,这也可以很容易地检查函数名称。然后,因为它只是一个对象,你可以这样做:

var myRouter = Backbone.Router.extend({
    routes: ... /* as usual */
    }).extend(Pages);       

你已经完成了。

这样做的一个很好的优点是它不会弄乱Backbone原型,所以即使版本更新发生了变化,它也不会让你感到厌烦。

答案 8 :(得分:0)

经过更多的操作之后。我找到了一个我在下面给出的解决方案...... 在这里你原来的根功能...

route: function(route, name, callback) { if (!_.isRegExp(route)) route = this._routeToRegExp(route); if (_.isFunction(name)) { callback = name; name = ''; } if (!callback) callback = this[name]; var router = this; Backbone.history.route(route, function(fragment) { var args = router._extractParameters(route, fragment); callback && callback.apply(router, args); router.trigger.apply(router, ['route:' + name].concat(args)); router.trigger('route', name, args); Backbone.history.trigger('route', router, name, args); }); return this; }

现在看看这个代码&将“route”功能更改为原始的Backbone.js ...

  route: function(route, name, callback) {

  if (!_.isRegExp(route)) route = this._routeToRegExp(route);
  if (_.isFunction(name)) {
    callback = name;
    name = '';
  }

  if (!callback) callback = this[name];
  var router = this;


  Backbone.history.route(route, function(fragment) {    
                   // takes matched route & fragment as like 'route1'
  var args = router._extractParameters(route, fragment); 
                  // extracts arguments if exists

 // here yours self invoking function or other function starts....

(function(){
                             // do something
 if ( true )                 // condition satisfies then route to the given Route
    {
     callback && callback.apply(router, args); 
    }
  else{
     name='route2';          // change name of route
     window.location.hash = 'route2';
     callback= function(){
                             // optional callback if u want
      }
     callback && callback.apply(router, args);  // route to ur custome Route
    }

 })();


  });
  return this;
}

-----谢谢-------- 爱2写脏代码! @xy ....

答案 9 :(得分:0)

这是一个适用于我所拥有的JavaScript版本;

var rp = Backbone.Router.prototype;
rp.routeWithoutEvents = rp.route;
rp.route = function(route, name, callback) {
    if (!callback) callback = this[name];
    this.routeWithoutEvents(route, name, function() {
        this.before.apply(this);
        callback.apply(this,arguments);
        this.after.apply(this);
    });
};

它基于Alexey Petrushin和Jonathan Tran的解决方案。

答案 10 :(得分:0)

在调用路由处理程序之前,我找不到一种简单的方法来拦截路由事件。

我的解决方案是扩展路由器组件,添加registerBeforeRouting方法并编辑路由方法(我从Backbone 1.0中获取它并且它起作用,YMMV具有不同的Backbone版本)。

在创建路由器之前:

var rp = Backbone.Router.prototype;

rp.registerBeforeRouting = function (callback) {
    this._beforeRoutingCallback = callback;
};

rp.route = function (route, name, callback) {
  if (!_.isRegExp(route)) route = this._routeToRegExp(route);
  if (_.isFunction(name)) {
    callback = name;
    name = '';
  }
  if (!callback) callback = this[name];
  var router = this;
  Backbone.history.route(route, function(fragment) {
    var args = router._extractParameters(route, fragment);
            // Edit starts here
    // This will trigger the callback previously set
    if (typeof router._beforeRoutingCallback === 'function') {
        router._beforeRoutingCallback();
    }
            // Edit stops here.

    callback && callback.apply(router, args);
    router.trigger.apply(router, ['route:' + name].concat(args));
    router.trigger('route', name, args);
    Backbone.history.trigger('route', router, name, args);
  });
  return this;
}

然后,在路由器初始化期间:

this.registerBeforeRouting(function() {
   console.log("Hello world");
});

答案 11 :(得分:0)

我尝试了前面提到的方法,但它们在某种程度上对我不起作用(可能是因为我对骨干和一般的javascript都缺乏深入的了解)。 我确实设法以其他方式做这个伎俩,如果那对任何人有兴趣的话:

我实际上最终做的只是扩展视图并重写渲染功能一次。

MyApp.BindedView = Backbone.View.extend({
    _realRender : null,
    initialize : function(){

        //validating user is logged in:
        if(Backbone.history.fragment != 'login' && !myUser.authenticated())
        {
            console.log('not authorized, redirecting');
            var self = this;
            this._realRender = this.render;
            this.render = function(route,name,callback){
                appRouter.navigate('login');
                self.render = self._realRender;
            }
            return;
        }

        this.delegateEvents();
    }
});

答案 12 :(得分:0)

添加了execute方法以便为此目的覆盖。请参阅从backbonejs主页中提取的此示例:

var Router = Backbone.Router.extend({
  execute: function(callback, args, name) {
    if (!loggedIn) {
      goToLogin();
      return false;
    }
    args.push(parseQueryString(args.pop()));
    if (callback) callback.apply(this, args);
  }
});