2011-09-12 5 views

risposta

9

_.wrap non è una soluzione, se si dispone ad esempio di 20 percorsi, è necessario avvolgerli tutti.

Ma si può fare questo con metaprogrammazione

class Backbone.FlexRouter extends Backbone.Router 
    route: (route, name, handler) -> 
    super route, name, -> 
     @trigger "route:before" 
     handler() 
     @trigger "route:after" 

UPD: Credo in JS dovrebbe essere qualcosa di simile (ma non ho provato)

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") 
    }) 
} 
+5

Sembra una soluzione solida, ma ho un problema, non riesco a leggere il caffè. C'è un modo per tradurre questo in javascript. Ho provato il compilatore online, ma questo rende il codice ancora più illeggibile per me. –

+1

Non ha funzionato per me, ma ho trovato un'altra soluzione. –

+2

@AmebaSpugnosa Link alla soluzione? – kehers

3

risposta di Alexey è quasi giusto, ma ci sono un paio di cose sottili che mancano.

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 
+0

La tua risposta è più precisa, ma potrebbe non funzionare poiché non è possibile fornire la richiamata. In tal caso, il metodo di instradamento per impostazione predefinita cerca una funzione denominata come nome della rotta (vedere il codice nella mia risposta). –

+0

@AmebaSpugnosa questa riga lo gestisce, quindi non sono sicuro di cosa intendi: 'callback = @ [nome] if! callback' –

+0

Se vai sul codice sorgente di Backbone.js, vedrai che Router.route non si limita a preparare un semplice callback. Il callback stesso, in determinate condizioni, può essere trovato dalla convenzione di denominazione e non dal passaggio di riferimento. Ecco perché non puoi semplicemente avvolgerlo come hai fatto tu. –

1

mi sono imbattuto in questo problema in precedenza e ho pensato di condividere la mia soluzione per l'inserimento di "middleware" nel flusso di routing Backbone. L'obiettivo era quello di reindirizzare gli utenti a vari flussi in base a una certa condizione, ad esempio, funzionalità bandiere, gestione delle sessioni, ecc ..

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); 
    }); 
    } 
}); 

Da lì si può semplicemente estendere la Backbone.ProtectedRouter con un protectedRoutes hash come così:

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

In questo scenario, se viene effettuata una richiesta per il percorso home e is_logged_in è false, il metodo renderLogin viene richiamato e passato 'some_arg'. Dopo il flusso, renderLogin restituisce un rinviato risolto che provoca l'invocazione del gestore originale (renderHome).

Spero che questo aiuti. Sono molto aperto anche ai suggerimenti! :)

1

Mi sono imbattuto recentemente in questa esigenza (per verificare che l'utente sia autenticato). Sfortunatamente Backbone non ci dà un evento prima/dopo, quindi dovrai sovrascrivere o estendere Router.route. Non sembra molto pulito dato che devi copiare dal codice sorgente e modificarlo, ma è l'unico modo che ho trovato. Qui di seguito il codice Backbone predefinito (1.0.0) e segnato la mia codice personalizzato:

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; 
    }; 

Avviso _.wrap e _.bind così this è quello che ci si aspetta quando si utilizza il router. In caso contrario, ricevevo un errore "questo non è definito".

0

ethnagnawl e Alexey sono entrambi corretti; _.wrap è la soluzione giusta ma se hai un sacco di percorsi e li scrivi nella normale dorsale, sarà un problema.Mi sono reso conto che si può fare questo:

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

Invece di definire i gestori del percorso direttamente in Router.extend, caricarli in un oggetto e poi fare questo:

_.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             
    });                    
});          

Questo rende anche abbastanza facile da controllare il nome della funzione se è necessario trattare un sottoinsieme di essi in modo diverso o qualcosa del genere. Quindi, poiché è solo un oggetto, puoi fare ciò:

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

E il gioco è fatto.

Uno dei vantaggi di questo è che non comporta alcun problema con i prototipi di Backbone, quindi anche se un aggiornamento di versione cambia qualcosa, non ti morderà.

0

dopo aver eseguito molte più manipolazioni. sono arrivato a una soluzione che ho dato al di sotto ...... Qui ur funzione radice originaria ...

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; }

Ora un'occhiata a questo codice & modificare la funzione "percorso" al tuo Backbone originale 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; 
} 

----- Thank You -------- Amore 2 scrittura Codici sporchi! @xy ....

0

Ecco una versione JavaScript che funziona con quello che ho;

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); 
    }); 
}; 

Si basa sulle soluzioni di Alexey Petrushin e Jonathan Tran.

6

Ecco la semplice, ignorando l'Backbone.Router sé

(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); 

Focus sulle linee

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

E

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

È possibile modificare le linee secondo la vostra bisogni

E qui è il codice del client utilizzando la nuova classe Backbone.Router

var appRouter = Backbone.Router.extend({ 

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

}); 
+1

Ben fatto. Grazie mille. – Trip

0

non riuscivo a trovare un modo semplice per intercettare l'evento di routing prima che il conduttore percorso si chiama.

La mia soluzione è di estendere il componente Router, aggiungendo un metodo registerBeforeRouting e modificando il metodo route (l'ho preso da Backbone 1.0 e ha funzionato, YMMV con diverse versioni Backbone).

prima della creazione del router:

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; 
} 

Poi, durante l'inizializzazione del router:

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

Ho provato gli approcci di cui sopra, e in qualche modo non ha funzionato per me (probabilmente per la mia mancanza di una comprensione approfondita né di backbone, né javascript in generale). Sono riuscito a fare il trucco in qualche altro modo, se questo è di interesse per chiunque là fuori:

Quello che in realtà finisco per fare è stato semplicemente estendere la vista e sovrascrivere la funzione di rendering solo una volta.

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

Il metodo execute è stato aggiunto per essere sostituito per questo scopo. Vedi questo esempio estratto dalla homepage di 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); 
    } 
});