Backbone.js: obtenir l'itinéraire actuel

137

En utilisant Backbone, est-il possible pour moi d'obtenir le nom de l'itinéraire actuel? Je sais comment me lier aux événements de changement d'itinéraire, mais j'aimerais être en mesure de déterminer l'itinéraire actuel à d'autres moments, entre les changements.

Drew Dara-Abrams
la source
Par "nom", voulez-vous dire la fonction à laquelle cette route se lie ou simplement quelle est l'URL actuelle ou la partie hachée de l'URL?
John Munsch le
1
Oh, oui, j'aurais dû être plus précis. J'aimerais connaître le nom de la fonction. (Je sais comment obtenir la partie hachée de l'URL en faisant location.hash ou Backbone.history.fragment.)
Drew Dara-Abrams

Réponses:

209

Si vous avez instancié un routeur dans votre application, la ligne suivante renvoie le fragment actuel:

Backbone.history.getFragment();

À partir de la documentation Backbone.js :

"[...] L'historique sert de routeur global (par image) pour gérer les événements de hachage ou pushState, faire correspondre la route appropriée et déclencher des rappels. Vous ne devriez jamais avoir à en créer un vous-même - vous devriez utiliser la référence à Backbone.history qui sera créé automatiquement pour vous si vous utilisez des routeurs avec des routes. [...] "

Si vous avez besoin du nom de la fonction liée à ce fragment, vous pouvez créer quelque chose comme ceci dans la portée de votre routeur:

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

Ou comme ceci depuis l'extérieur de votre routeur:

alert( myRouter.routes[Backbone.history.getFragment()] );
Robert
la source
Merci, c'est super. Je n'ai jamais semblé mentionner Backbone.history.fragment dans le document. Est-ce un nouvel ajout ou simplement non documenté?
Drew Dara-Abrams
Je pense que ce n'est pas documenté. En fait, je ne me souviens pas où je l'ai vu pour la première fois, un blog ou peut-être le code source de la dorsale.
Robert
25
Pour info, cela ne fonctionne pas avec des routes comme 'foo /: id' ou 'bar * params'
wprl
2
Meilleure utilisation Backbone.history.getFragment(), car Backbone.history.fragmentc'est une propriété privée cachée.
Vad
1
J'ai corrigé la réponse suite à la suggestion @Vad. Je n'utilise plus Backbone mais son commentaire me convient. (La réponse précédente était: Backbone.history.fragment au lieu de Backbone.history.getFragment ())
Robert
57

La réponse de Robert est intéressante, mais malheureusement elle ne fonctionnera que si le hachage est exactement comme défini dans l'itinéraire. Si, par exemple, vous avez une route pour user(/:uid)elle ne sera pas mise en correspondance si le Backbone.history.fragmentest l'un "user"ou l' autre "user/1"(qui sont les deux cas d'utilisation les plus évidents pour une telle route). En d'autres termes, il ne trouvera le nom de rappel approprié que si le hachage est exactement "user(/:uid)"(hautement improbable).

Comme j'avais besoin de cette fonctionnalité, j'ai étendu le Backbone.Routeravec une currentfonction qui réutilise une partie du code utilisé par l'objet History et Router pour faire correspondre le fragment actuel aux routes définies pour déclencher le rappel approprié. Pour mon cas d'utilisation, il prend le paramètre facultatif route, qui, s'il est défini sur quelque chose de véridique, renverra le nom de fonction correspondant défini pour l'itinéraire. Sinon, il retournera le fragment de hachage actuel de Backbone.History.fragment.

Vous pouvez ajouter le code à votre extension existante où vous initialisez et configurez le routeur 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'

Je suis sûr que certaines améliorations pourraient être apportées, mais c'est un bon moyen de vous lancer. En outre, il est facile de créer cette fonctionnalité sans étendre l'objet Route. Je l'ai fait parce que c'était le moyen le plus pratique pour ma configuration.

Je n'ai pas encore testé cela complètement, alors faites-moi savoir si quelque chose va au sud.


MISE À JOUR 25/04/2013

J'ai apporté quelques modifications à la fonction, donc au lieu de renvoyer le hachage ou le nom de rappel de la route, je renvoie un objet avec un fragment, des paramètres et une route afin que vous puissiez accéder à toutes les données de la route actuelle, un peu comme vous le feriez de la route- un événement.

Vous pouvez voir les changements ci-dessous:

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

Voir le code précédent pour plus de commentaires et d'explications, ils se ressemblent pour la plupart.

Simon Kjellberg
la source
1
J'étais sur le point d'écrire cela moi-même, mais j'ai d'abord cherché la solution sur Google. Heureux que je l'ai fait. Le vôtre fait exactement ce que je voulais, merci!
Dan Abramov
Voici une version un peu plus détaillée qui, à mon avis, est plus facile à comprendre à première vue.
Dan Abramov
4
Wow, merci pour ça! Vous venez de recevoir un accusé de réception dans notre base de code. ; P Nous l'avons légèrement modifié pour Marionette. En passant, en tant que raccourci, vous pouvez définir le troisième paramètre (définit le contexte) de la _.findfonction comme this, éliminant ainsi le besoin de la Routervariable (@DanAbramov l'a déjà fait).
Mark
@Mark Ce sont de bonnes nouvelles. Mais comment puis-je utiliser cette fonctionnalité dans marionnette? Il n'y a rien à ce sujet dans la documentation.
darksoulsong
2
@darksoulsong Si vous utilisez Marionette.AppRouter, étendez votre routeur avec la fonction ci-dessus, mais remplacez-la Router.appRoutespar Router.routes.
Mark
11

Pour obtenir l'itinéraire appelant (ou l'URL) du gestionnaire d'itinéraire appelé, vous pouvez l'obtenir en vérifiant

Backbone.history.location.href ... l'url complète
Backbone.history.location.search ... chaîne de requête à partir de?

Je suis arrivé ici à la recherche de cette réponse, alors je suppose que je devrais laisser ce que j'ai trouvé.

yoshi
la source
6

Si vous utilisez le paramètre racine du routeur, vous pouvez également l'inclure pour obtenir le «vrai» fragment.

(Backbone.history.options.root || "") + "/" + Backbone.history.fragment
user1310662
la source
6

Voici une version un peu plus verbeuse (ou, selon votre goût, plus lisible) de la réponse de 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
    };
  }
}
Dan Abramov
la source
4

Si vous regardez la source pour le Router, vous verrez que lorsque le routeur déclenche l'événement disant que quelque chose change, il passe le nom avec lui comme "route: nom".

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

Vous pouvez toujours accrocher l'événement "route" sur le routeur et le stocker pour obtenir l'itinéraire actuel.

Brian Genisio
la source
OK, je suppose que c'est une sorte de "construit vous-même".
Drew Dara-Abrams le
Ouais, mais vous n'obtiendrez pas d' routeévénement si ce trigger n'est pas le cas true (recommandé et par défaut).
Dan Abramov