Backbone.js: ottieni il percorso corrente


137

Utilizzando Backbone, è possibile ottenere il nome del percorso corrente? So come associare gli eventi di modifica del percorso, ma mi piacerebbe essere in grado di determinare il percorso corrente altre volte, tra le modifiche.


Per "nome" intendi la funzione a cui si lega quella rotta o qual è l'URL corrente o la parte hash dell'URL?
John Munsch,

1
Oh, sì, avrei dovuto essere più specifico. Vorrei sapere il nome della funzione. (So ​​come ottenere la parte hash dell'URL facendo location.hash o Backbone.history.fragment.)
Drew Dara-Abrams,

Risposte:


209

Se è stata creata un'istanza di un router nell'applicazione, la riga seguente restituisce il frammento corrente:

Backbone.history.getFragment();

Dalla documentazione Backbone.js :

"[...] La cronologia funge da router globale (per frame) per gestire gli eventi di hashchange o pushState, abbinare il percorso appropriato e attivare i callback. Non dovresti mai crearne uno tu stesso - dovresti usare il riferimento alla Backbone.history che verrà creata automaticamente se utilizzi router con route. [...] "

Se è necessario il nome della funzione associato a quel frammento, è possibile creare qualcosa del genere all'interno dell'ambito del router:

alert( this.routes[Backbone.history.getFragment()] );

O in questo modo dall'esterno del router:

alert( myRouter.routes[Backbone.history.getFragment()] );

Grazie, fantastico. Non mi è mai sembrato alcun accenno a Backbone.history.fragment nel documento. È una nuova aggiunta o semplicemente non documentata?
Drew-Abrams,

Penso che questo non sia documentato. In realtà, non ricordo dove l'ho visto per la prima volta, qualche blog o forse il codice sorgente della spina dorsale.
Robert,

25
Cordiali saluti, questo non funziona con percorsi come 'foo /: id' o 'bar * params'
wprl

2
Meglio usare Backbone.history.getFragment(), perché Backbone.history.fragmentè una proprietà privata nascosta.
Vad

1
Ho corretto la risposta seguendo il suggerimento @Vad. Non sto più usando Backbone ma il suo commento suona bene per me. (La risposta precedente era: Backbone.history.fragment invece di Backbone.history.getFragment ())
Robert

57

La risposta di Robert è interessante, ma purtroppo funzionerà solo se l'hash è esattamente come definito nella rotta. Ad esempio, se si dispone di un percorso, user(/:uid)esso non verrà abbinato se Backbone.history.fragmentè uno "user"o "user/1"(entrambi sono i due casi d'uso più ovvi per tale percorso). In altre parole, troverà il nome di richiamata appropriato solo se l'hash è esattamente "user(/:uid)"(altamente improbabile).

Dato che avevo bisogno di questa funzionalità, ho esteso Backbone.Routercon una funzione currentche riutilizza parte del codice utilizzato dall'oggetto History and Router per far corrispondere il frammento corrente ai percorsi definiti per attivare il callback appropriato. Nel mio caso d'uso, accetta il parametro opzionale route, che se impostato su qualcosa di vero restituirà il nome della funzione corrispondente definito per il percorso. Altrimenti restituirà l'attuale frammento di hash da Backbone.History.fragment.

È possibile aggiungere il codice all'estensione esistente in cui si inizializza e configura il router Backbone.

var Router = new Backbone.Router.extend({

    // Pretty basic stuff
    routes : {
        "home" : "home",
        "user(:/uid)" : "user",
        "test" : "completelyDifferent"
    },

    home : function() {
        // Home route
    },

    user : function(uid) {
        // User route
    },

    // Gets the current route callback function name
    // or current hash fragment
    current : function(route){
        if(route && Backbone.History.started) {
            var Router = this,
                // Get current fragment from Backbone.History
                fragment = Backbone.history.fragment,
                // Get current object of routes and convert to array-pairs
                routes = _.pairs(Router.routes);

            // Loop through array pairs and return
            // array on first truthful match.
            var matched = _.find(routes, function(handler) {
                var route = handler[0];

                // Convert the route to RegExp using the 
                // Backbone Router's internal convert
                // function (if it already isn't a RegExp)
                route = _.isRegExp(route) ? route :  Router._routeToRegExp(route);

                // Test the regexp against the current fragment
                return route.test(fragment);
            });

            // Returns callback name or false if 
            // no matches are found
            return matched ? matched[1] : false;
        } else {
            // Just return current hash fragment in History
            return Backbone.history.fragment
        }
    }
});

// Example uses:
// Location: /home
// console.log(Router.current()) // Outputs 'home'
// Location: /user/1
// console.log(Router.current(true)) // Outputs 'user'
// Location: /user/2
// console.log(Router.current()) // Outputs 'user/2'
// Location: /test
// console.log(Router.current(true)) // Outputs 'completelyDifferent'

Sono sicuro che potrebbero essere apportati alcuni miglioramenti, ma questo è un buon modo per iniziare. Inoltre, è facile creare questa funzionalità senza estendere l'oggetto Route. L'ho fatto perché era il modo più conveniente per il mio set-up.

Non l'ho ancora testato completamente, quindi per favore fatemi sapere se qualcosa va a sud.


AGGIORNAMENTO 25/04/2013

Ho apportato alcune modifiche alla funzione, quindi invece di restituire il nome di callback di hash o route, restituisco un oggetto con frammento, parametri e route in modo da poter accedere a tutti i dati dalla route corrente, proprio come faresti dalla route- evento.

Puoi vedere le modifiche qui sotto:

current : function() {
    var Router = this,
        fragment = Backbone.history.fragment,
        routes = _.pairs(Router.routes),
        route = null, params = null, matched;

    matched = _.find(routes, function(handler) {
        route = _.isRegExp(handler[0]) ? handler[0] : Router._routeToRegExp(handler[0]);
        return route.test(fragment);
    });

    if(matched) {
        // NEW: Extracts the params using the internal
        // function _extractParameters 
        params = Router._extractParameters(route, fragment);
        route = matched[1];
    }

    return {
        route : route,
        fragment : fragment,
        params : params
    };
}

Vedi il codice precedente per ulteriori commenti e spiegazioni, sembrano quasi uguali.


1
Stavo per scriverlo da solo, ma prima ho cercato su Google la soluzione. Sono contento di averlo fatto. Il tuo fa esattamente quello che volevo, grazie!
Dan Abramov,

Ecco una versione leggermente più dettagliata che credo sia più facile da capire a prima vista.
Dan Abramov,

4
Wow, grazie per questo! Hai appena ricevuto un riconoscimento nella nostra base di codice. ; P Lo abbiamo leggermente modificato per Marionette. A proposito, come scorciatoia, puoi impostare il terzo parametro (imposta il contesto) della _.findfunzione come this, eliminando così la necessità della Routervariabile (lo ha già fatto @DanAbramov).
Segna il

@Mark Queste sono buone notizie. Ma come posso usare questa funzione nella marionetta? Non c'è nulla al riguardo nei documenti.
darksoulsong,

2
@darksoulsong Se stai usando Marionette.AppRouter, estendere il router con la funzione di cui sopra, ma sostituire Router.appRoutesper Router.routes.
Marco

11

Per ottenere la route di chiamata (o url) dal gestore di route chiamato, è possibile ottenerlo controllando

Backbone.history.location.href ... l'URL completo
Backbone.history.location.search ... stringa di query a partire da?

Sono arrivato qui alla ricerca di questa risposta, quindi credo che dovrei lasciare ciò che ho trovato.


6

Se si utilizza l'impostazione di root per il router, è anche possibile includerla per ottenere il frammento "reale".

(Backbone.history.options.root || "") + "/" + Backbone.history.fragment

6

Ecco una versione un po ' più dettagliata (o, a seconda dei tuoi gusti, più leggibile) della risposta di Simon :

current: function () {
  var fragment = Backbone.history.fragment,
      routes = _.pairs(this.routes),
      route,
      name,
      found;

  found = _.find(routes, function (namedRoute) {
    route = namedRoute[0];
    name = namedRoute[1];

    if (!_.isRegExp(route)) {
      route = this._routeToRegExp(route);
    }

    return route.test(fragment);
  }, this);

  if (found) {
    return {
      name: name,
      params: this._extractParameters(route, fragment),
      fragment: fragment
    };
  }
}

4

Se guardi l'origine per Router, vedrai che quando il router attiva l'evento dicendo che qualcosa cambia, passa il nome con esso come "route: name".

http://documentcloud.github.com/backbone/docs/backbone.html#section-84

Puoi sempre agganciare l'evento "route" sul router e memorizzarlo per ottenere il percorso corrente.


OK, immagino sia una cosa "costruita da te".
Ha disegnato Dara-Abrams il

Sì, ma non otterrai l' routeevento se trigger non lo è true (consigliato e predefinito).
Dan Abramov,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.