Backbone.js - вызов метода до / после запуска маршрута - PullRequest
9 голосов
/ 13 сентября 2011

Я хочу, чтобы метод setup / teardown вызывался до и после запуска маршрута в моем маршрутизаторе Backbone.js соответственно. Кто-нибудь создал элегантный способ сделать это?

Ответы [ 13 ]

9 голосов
/ 08 октября 2011

_. Обтекание не является решением, если у вас есть, например, 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")
  })
}
8 голосов
/ 13 сентября 2011

Рассматривали ли вы _. Wrap ?

6 голосов
/ 24 октября 2013

Вот простой, переопределяющий 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 голосов
/ 29 марта 2013

Ответ Алексея почти верен, но есть несколько тонких вещей, которые отсутствуют.

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
1 голос
/ 30 апреля 2013

Я недавно столкнулся с этой необходимостью (для проверки подлинности пользователя).К сожалению, Backbone не дает нам событие до / после, поэтому вам нужно будет перезаписать или расширить 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 - это то, что вы ожидаете при использовании маршрутизатора.В противном случае я получил ошибку «это не определено».

1 голос
/ 25 апреля 2013

Я сталкивался с этой проблемой ранее и думал, что поделюсь своим решением для вставки «промежуточного программного обеспечения» в поток маршрутизации 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_in равен false, метод renderLogin вызывается и передается 'some_arg'. После потока renderLogin вернет разрешенное значение Deferred, которое вызывает исходный обработчик (renderHome).

Надеюсь, это поможет. Я очень открыт для предложений! :)

1 голос
/ 19 марта 2012

Этот плагин делает то, что вы хотите. Работает с 0.5.3. Я не уверен, работает ли он с 0.9.1 или нет.

https://github.com/angelo0000/backbone_filters

0 голосов
/ 22 ноября 2015

Для этой цели был добавлен метод 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);
  }
});
0 голосов
/ 15 мая 2014

Я попробовал вышеупомянутые подходы, и они почему-то просто не работали для меня (вероятно, из-за моего глубокого понимания ни основы, ни javascript в целом).Мне удалось сделать трюк каким-то другим способом, если это кому-то интересно:

В действительности я в итоге просто расширил View и переопределил функцию рендера всего один раз.

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();
    }
});
0 голосов
/ 17 ноября 2013

Я не смог найти простой способ перехватить событие маршрутизации до вызова обработчика маршрута.

Мое решение - расширить компонент Router, добавив метод registerBeforeRouting и отредактировав метод route (я взял его из 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");
});
...