Backbone.js: Aktuelle Route abrufen


137

Kann ich mit Backbone den Namen der aktuellen Route abrufen? Ich weiß, wie man sich an Routenänderungsereignisse bindet, möchte aber in der Lage sein, die aktuelle Route zu anderen Zeiten zwischen Änderungen zu bestimmen.


Mit "Name" meinen Sie die Funktion, an die diese Route gebunden ist, oder nur die aktuelle URL oder den Hash-Teil der URL?
John Munsch

1
Oh ja, ich hätte genauer sein sollen. Ich möchte den Namen der Funktion wissen. (Ich weiß, wie man den Hash-Teil der URL erhält, indem man location.hash oder Backbone.history.fragment ausführt.)
Drew Dara-Abrams

Antworten:


209

Wenn Sie in Ihrer Anwendung einen Router instanziiert haben, gibt die folgende Zeile das aktuelle Fragment zurück:

Backbone.history.getFragment();

Aus der Dokumentation zu Backbone.js :

"[...] Der Verlauf dient als globaler Router (pro Frame), um Hash-Change-Ereignisse oder PushState zu verarbeiten, die entsprechende Route abzugleichen und Rückrufe auszulösen. Sie sollten niemals eine dieser Routen selbst erstellen müssen - Sie sollten die Referenz verwenden zu Backbone.history, die automatisch für Sie erstellt wird, wenn Sie Router mit Routen verwenden. [...] "

Wenn Sie den Namen der an dieses Fragment gebundenen Funktion benötigen, können Sie im Rahmen Ihres Routers Folgendes erstellen:

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

Oder so von außerhalb Ihres Routers:

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

Danke, das ist großartig. Ich habe Backbone.history.fragment im Dokument nie erwähnt. Ist dies ein Neuzugang oder nur undokumentiert?
Drew Dara-Abrams

Ich denke, das ist nicht dokumentiert. Eigentlich erinnere ich mich nicht, wo ich es zum ersten Mal gesehen habe, ein Blog oder vielleicht den Backbone-Quellcode.
Robert

25
Zu Ihrer
Information

2
Bessere Verwendung Backbone.history.getFragment(), da Backbone.history.fragmentes sich um ein privates verstecktes Eigentum handelt.
Vad

1
Ich habe die Antwort nach dem @ Vad-Vorschlag korrigiert. Ich benutze Backbone nicht mehr, aber sein Kommentar klingt für mich richtig. (Die vorherige Antwort war: Backbone.history.fragment anstelle von Backbone.history.getFragment ())
Robert

57

Roberts Antwort ist interessant, aber leider funktioniert sie nur, wenn der Hash genau der Definition in der Route entspricht. Wenn Sie beispielsweise eine Route user(/:uid)haben, wird diese nicht abgeglichen, wenn Backbone.history.fragmententweder "user"oder "user/1"(beides sind die beiden offensichtlichsten Anwendungsfälle für eine solche Route). Mit anderen Worten, der passende Rückrufname wird nur gefunden, wenn der Hash genau ist "user(/:uid)"(höchst unwahrscheinlich).

Da ich diese Funktionalität benötigte, erweiterte ich die um Backbone.Routereine Funktion, die einen currentTeil des Codes wiederverwendet, den das Verlaufs- und Router-Objekt verwendet, um das aktuelle Fragment mit den definierten Routen abzugleichen, um den entsprechenden Rückruf auszulösen. Für meinen Anwendungsfall wird der optionale Parameter verwendet route, der bei Einstellung auf "true" den entsprechenden für die Route definierten Funktionsnamen zurückgibt. Andernfalls wird das aktuelle Hash-Fragment von zurückgegeben Backbone.History.fragment.

Sie können den Code zu Ihrem vorhandenen Extend hinzufügen, in dem Sie den Backbone-Router initialisieren und einrichten.

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'

Ich bin sicher, dass einige Verbesserungen vorgenommen werden könnten, aber dies ist ein guter Weg, um Ihnen den Einstieg zu erleichtern. Es ist auch einfach, diese Funktionalität zu erstellen, ohne das Route-Objekt zu erweitern. Ich habe dies getan, weil es der bequemste Weg für mein Setup war.

Ich habe dies noch nicht vollständig getestet. Bitte lassen Sie mich wissen, wenn etwas nach Süden geht.


UPDATE 25.04.2013

Ich habe einige Änderungen an der Funktion vorgenommen. Anstatt entweder den Hash- oder den Routenrückrufnamen zurückzugeben, gebe ich ein Objekt mit Fragment, Parametern und Route zurück, damit Sie auf alle Daten der aktuellen Route zugreifen können, ähnlich wie Sie es von der Route tun würden. Veranstaltung.

Sie können die Änderungen unten sehen:

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

Weitere Kommentare und Erklärungen finden Sie im vorherigen Code. Sie sehen größtenteils gleich aus.


1
Ich wollte das gerade selbst schreiben, googelte aber zuerst nach der Lösung. Ich bin froh, dass ich es getan habe. Ihr macht genau das , was ich wollte, danke!
Dan Abramov

Hier ist eine etwas ausführlichere Version , die meiner Meinung nach auf den ersten Blick leichter zu verstehen ist.
Dan Abramov

4
Wow, danke dafür! Sie haben gerade eine Bestätigung in unserer Codebasis erhalten. ; P Wir haben es für Marionette leicht angepasst. Übrigens können Sie als Verknüpfung den dritten Parameter (legt den Kontext fest) der _.findFunktion als festlegen this, wodurch die Notwendigkeit für die RouterVariable entfällt (@DanAbramov hat dies bereits getan).
Mark

@ Mark Das sind gute Nachrichten. Aber wie kann ich diese Funktion in Marionette verwenden? In den Dokumenten ist nichts darüber.
Darksoulsong

2
@darksoulsong Wenn Sie verwenden Marionette.AppRouter, erweitern Sie Ihren Router mit der Funktion oben, aber Ersatz Router.appRoutesfür Router.routes.
Mark

11

Um die aufrufende Route (oder URL) vom Handler für aufgerufene Routen abzurufen, können Sie sie durch Überprüfen abrufen

Backbone.history.location.href ... die vollständige URL
Backbone.history.location.search ... Abfragezeichenfolge ab?

Ich bin auf der Suche nach dieser Antwort hierher gekommen, also sollte ich das, was ich gefunden habe, verlassen.


6

Wenn Sie die Root-Einstellung für den Router verwenden, können Sie sie auch einschließen, um das "echte" Fragment zu erhalten.

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

6

Hier ist ein bisschen ausführliche (oder, je nach Geschmack, besser lesbar) Version von Simon Antwort :

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

Wenn Sie sich die Quelle für das ansehen Router, werden Sie feststellen, dass der Router, wenn er das Ereignis auslöst, dass sich etwas ändert, den Namen als "route: name" übergibt.

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

Sie können das Ereignis "route" jederzeit in den Router einbinden und speichern, um die aktuelle Route abzurufen.


OK, ich denke, das ist so etwas wie "selbst gebaut".
Drew Dara-Abrams

Ja, aber Sie erhalten kein routeEreignis, wenn dies trigger nicht der Fall isttrue (empfohlen und Standard).
Dan Abramov
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.