Backbone.js: obtenha a rota atual


137

Usando o Backbone, é possível obter o nome da rota atual? Eu sei como vincular eventos de alteração de rota, mas gostaria de poder determinar a rota atual em outros momentos, entre as alterações.


Com "nome", você quer dizer a função à qual essa rota se liga ou apenas qual é o URL atual ou parte de hash do URL?
John Munsch

1
Ah, sim, eu deveria ter sido mais específico. Eu gostaria de saber o nome da função. (Eu sei como obter a parte de hash da URL, fazendo location.hash ou Backbone.history.fragment.) #
434 Drew Dara-Abrams

Respostas:


209

Se você instancia um roteador em seu aplicativo, a seguinte linha retornará o fragmento atual:

Backbone.history.getFragment();

Na documentação do Backbone.js :

"[...] O histórico serve como um roteador global (por quadro) para manipular eventos de alteração de hash ou pushState, corresponder à rota apropriada e acionar retornos de chamada. Você nunca precisará criar um desses você mesmo - use a referência para Backbone.history que será criado automaticamente para você se você usar roteadores com rotas. [...] "

Se você precisar do nome da função vinculada a esse fragmento, poderá criar algo assim dentro do escopo do seu roteador:

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

Ou assim de fora do seu roteador:

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

Obrigado, isso é ótimo. Nunca pareço nenhuma menção a Backbone.history.fragment no documento. É uma nova adição ou apenas não documentada?
Drew Dara-Abrams

Eu acho que isso não está documentado. Na verdade, não me lembro de onde vi pela primeira vez, algum blog ou talvez o código fonte da espinha dorsal.
Robert

25
FYI, este não funciona com rotas como 'foo /: id' ou 'bar * params'
wprl

2
Melhor uso Backbone.history.getFragment(), porque Backbone.history.fragmenté uma propriedade privada oculta.
Vad

1
Corrigi a resposta seguindo a sugestão do @Vad. Não estou mais usando o Backbone, mas o comentário dele parece certo para mim. (A resposta anterior era: Backbone.history.fragment vez de Backbone.history.getFragment ())
Robert

57

A resposta de Robert é interessante, mas infelizmente só funcionará se o hash for exatamente como definido na rota. Se, por exemplo, você tiver uma rota, user(/:uid)ela não corresponderá se Backbone.history.fragmenté "user"ou "user/1"(ambos os dois casos de uso mais óbvios para essa rota). Em outras palavras, ele encontrará apenas o nome de retorno de chamada apropriado se o hash for exatamente"user(/:uid)" (altamente improvável).

Como eu precisava dessa funcionalidade, estendi a função Backbone.Routerwith currentque reutiliza parte do código que o objeto History and Router usa para corresponder o fragmento atual às rotas definidas para acionar o retorno de chamada apropriado. Para o meu caso de uso, é necessário o parâmetro opcional route, que, se definido como algo verdadeiro, retornará o nome da função correspondente definido para a rota. Caso contrário, ele retornará o atual fragmento de hash deBackbone.History.fragment .

Você pode adicionar o código ao seu Extend existente, onde inicializa e configura o roteador 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'

Tenho certeza de que algumas melhorias podem ser feitas, mas essa é uma boa maneira de começar. Além disso, é fácil criar essa funcionalidade sem estender o objeto Route. Fiz isso porque era a maneira mais conveniente para a minha configuração.

Ainda não testei isso completamente, por favor, avise-me se algo der para o sul.


ATUALIZAÇÃO 25/04/2013

Fiz algumas alterações na função. Portanto, em vez de retornar o nome de retorno de chamada de hash ou rota, retorno um objeto com fragmentos, parâmetros e rota para que você possa acessar todos os dados da rota atual, como faria com a rota- evento.

Você pode ver as alterações abaixo:

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

Veja o código anterior para mais comentários e explicações, eles têm a mesma aparência.


1
Estava prestes a escrever isso sozinho, mas pesquisei primeiro a solução no Google. Fico feliz que eu fiz. O seu faz exatamente o que eu queria, obrigado!
Dan Abramov 21/01

Aqui está uma versão um pouco mais detalhada que acredito ser mais fácil de entender à primeira vista.
Dan Abramov 21/01

4
Uau, obrigado por isso! Você acabou de receber um reconhecimento em nossa base de código. ; P Ajustamos um pouco para Marionette. A propósito, como atalho, você pode definir o terceiro parâmetro (define o contexto) da _.findfunção como this, eliminando assim a necessidade da Routervariável (o @DanAbramov já fez isso).
Mark

@ Mark Estas são boas notícias. Mas como posso usar esse recurso na marionete? Não há nada sobre isso nos documentos.
darksoulsong

2
@darksoulsong Se você estiver usando Marionette.AppRouter, estenda seu roteador com a função acima, mas substitua Router.appRoutes-o Router.routes.
Mark

11

Para obter a rota (ou URL) de chamada do manipulador de rota chamado, você pode obtê-la verificando

Backbone.history.location.href ... o URL completo
Backbone.history.location.search ... sequência de consultas a partir de?

Cheguei aqui na busca desta resposta, então acho que devo deixar o que encontrei.


6

Se você usar a configuração raiz do roteador, também poderá incluí-la para obter o fragmento 'real'.

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

6

Aqui está uma versão um pouco mais detalhada (ou, dependendo do seu gosto, mais legível) da resposta 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
    };
  }
}

4

Se você procurar na fonte Router, verá que, quando o roteador aciona o evento dizendo que algo muda, ele passa o nome como "route: name".

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

Você sempre pode conectar o evento "route" ao roteador e armazená-lo para obter a rota atual.


OK, acho que esse é o tipo de coisa "construa você mesmo".
Drew Dara-Abrams

Sim, mas você não receberá um routeevento se trigger não for true (recomendado e padrão).
Dan Abramov 21/01
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.