Voglio un metodo di setup/teardown da chiamare prima e dopo un instradamento nel mio router Backbone.js, rispettivamente. Qualcuno ha creato un modo elegante per farlo?Backbone.js - Metodo di chiamata prima/dopo l'attivazione di un instradamento
risposta
_.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")
})
}
Avete considerato _.wrap?
Questo plugin fa quello tu vuoi. Funziona con 0.5.3. Non sono sicuro se funzioni ancora con 0.9.1 o no.
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
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). –
@AmebaSpugnosa questa riga lo gestisce, quindi non sono sicuro di cosa intendi: 'callback = @ [nome] if! callback' –
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. –
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! :)
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".
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à.
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 ....
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.
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;
}
});
Ben fatto. Grazie mille. – Trip
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");
});
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();
}
});
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);
}
});
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. –
Non ha funzionato per me, ma ho trovato un'altra soluzione. –
@AmebaSpugnosa Link alla soluzione? – kehers