Backbone: navegue() com um espaço no url aciona a rota mesmo quando {trigger:false} no Firefox

Criado em 27 nov. 2014  ·  8Comentários  ·  Fonte: jashkenas/backbone

Considere este exemplo:

$(function(){
var AppRouter = Backbone.Router.extend({

    routes: {
        '(count/:count)': 'home',
    },

    home: function(currCount) {
        var counter = 0;
        var thisObj = this;

        $("body").html($("<p>Click me</p>").on('click', function() {
            console.log("clicked "+counter);
            counter++;
            thisObj.navigate('count/'+counter, {trigger:false, replace: true});
        }));

        console.log('home route: '+currCount);    
    },
});

window.app = new AppRouter();
Backbone.history.start();
});

Isso deve registrar "rota inicial: null" e, em seguida, prosseguir para registrar "clique #" para cada clique em "Clique em mim".

Agora, faça uma pequena modificação. Adicione um espaço à url em browse():

thisObj.navigate('count/ '+counter, {trigger:false, replace: true});

Tente novamente e você descobrirá que a "rota inicial: #" também começará a ser registrada, e o contador será redefinido _dois de cada três cliques_.

Isso está na versão 1.1.2 e estou testando no Firefox 33.1 mais recente. Não estou tendo esse problema no Chrome - pode ser porque o Chrome talvez faça algo para codificar espaços no URL. Eu não tenho certeza.

Eu percebo que o caractere de espaço não deve estar em uma URL em primeiro lugar, mas esse comportamento é estranho e deve no mínimo ser documentado. (ou seja, "A navegação() pode não se comportar como esperado com URLs contendo espaços (ou outros caracteres não seguros?).")

Não tenho certeza se há uma solução fácil para isso, mas espero que pelo menos documentando esse bug eu economize tempo de outros desenvolvedores tentando caçá-lo.

invalid

Comentários muito úteis

Aqui está o que eu descobri. No Firefox, se você marcar a variável "this.location.href", quaisquer espaços em branco na variável serão codificados em %20's. O Chrome (e presumivelmente o IE) os deixa como espaço em branco. Este é um problema no Backbone, pois quando você vai navegar, "checkUrl()" faz uma comparação de "this.fragment" com o valor retornado de "this.getFragment()" para determinar se deve ou não chamar "this. loadUrl()", se não corresponderem. this.getFragment() retorna o valor de this.location.href, com %20's no lugar de qualquer espaço no Firefox (ou qualquer codificação para esse assunto). this.fragment retorna um equivalente decodificado. this.fragment permanece decodificado, independentemente de você usar ou não encodeURI() em seus URLs, porque na função "navigate()", this.fragment é atribuído chamando "this.decodeFragment()" no URL que é passado in, removendo qualquer tentativa de forçar a codificação.

Portanto, se this.fragment for sempre um URL decodificado devido ao uso de this.decodeFragment(), e o URL contiver quaisquer caracteres que encodeURI() mudaria (neste caso, espaços), ele nunca corresponderá ao valor retornado deste .getFragment() no Firefox, pois o Firefox codifica o valor de this.location.href, enquanto outros navegadores não. Como esses dois valores não correspondem, this.loadUrl() é chamado em checkUrl(), independentemente de o parâmetro de disparo em router.navigate ser verdadeiro ou falso.

A correção preliminar (teste pré-exaustivo) seria alterar o valor de retorno do método "getHash()" para ler:

return match ? this.decodeFragment(match[1].replace(pathStripper, '')) : '';

Todos 8 comentários

Olá @chaimpeck! Obrigado por relatar isso.

Infelizmente, não consegui reproduzi-lo usando o código acima. Eu tentei o Chrome e o Firefox mais recentes com o branch master e a versão 1.1.2. Você se importaria de postar um exemplo de trabalho em um gist/bin/fiddle que eu possa dar uma olhada?

Definitivamente, tivemos problemas com a codificação/decodificação de caracteres específicos no Firefox antes e não duvido que haja mais. :risonho:

Na verdade, eu vi exatamente esse mesmo problema, mas não consigo compartilhar meu código. Vou ver se consigo simular algum código de teste para replicar isso, mas descobri que o problema é definitivamente baseado no navegador. Todas as versões modernas do IE e do Chrome funcionam bem, mas pelo menos no Firefox v31.5.0 (não pergunte, eu odeio a infraestrutura em que estamos presos), ele definitivamente é acionado sem adicionar a opção de gatilho.

Aqui está o que eu descobri. No Firefox, se você marcar a variável "this.location.href", quaisquer espaços em branco na variável serão codificados em %20's. O Chrome (e presumivelmente o IE) os deixa como espaço em branco. Este é um problema no Backbone, pois quando você vai navegar, "checkUrl()" faz uma comparação de "this.fragment" com o valor retornado de "this.getFragment()" para determinar se deve ou não chamar "this. loadUrl()", se não corresponderem. this.getFragment() retorna o valor de this.location.href, com %20's no lugar de qualquer espaço no Firefox (ou qualquer codificação para esse assunto). this.fragment retorna um equivalente decodificado. this.fragment permanece decodificado, independentemente de você usar ou não encodeURI() em seus URLs, porque na função "navigate()", this.fragment é atribuído chamando "this.decodeFragment()" no URL que é passado in, removendo qualquer tentativa de forçar a codificação.

Portanto, se this.fragment for sempre um URL decodificado devido ao uso de this.decodeFragment(), e o URL contiver quaisquer caracteres que encodeURI() mudaria (neste caso, espaços), ele nunca corresponderá ao valor retornado deste .getFragment() no Firefox, pois o Firefox codifica o valor de this.location.href, enquanto outros navegadores não. Como esses dois valores não correspondem, this.loadUrl() é chamado em checkUrl(), independentemente de o parâmetro de disparo em router.navigate ser verdadeiro ou falso.

A correção preliminar (teste pré-exaustivo) seria alterar o valor de retorno do método "getHash()" para ler:

return match ? this.decodeFragment(match[1].replace(pathStripper, '')) : '';

Ainda é necessário com a última versão, então obrigado!

Eu tenho o mesmo problema ao tentar usar caracteres não padrão na URL, no meu caso åäö (caracteres que estão no alfabeto sueco). Última versão do Chrome e Backbone.

A solução do sjmiller85 funcionou para mim também. Muito obrigado!

@sjmiller85 Qual foi a sua solução aqui? Eu não entendo completamente o que deve ser usado para 'pathStripper'. Isso é um regex?
Edit: isso funciona na minha situação. veja https://github.com/jashkenas/backbone/pull/3955/files
getHash: function (t) { var e = (t || this).location.href.match(/#(.*)$/); return e ? e[1] : "" }

@Aggror pathStripper é, de acordo com a documentação, um regex em cache para remover URLs de hash.

// Cached regex for stripping urls of hash.
var pathStripper = /#.*$/;

Portanto, é o equivalente à sua solução, apenas mais organizada e eficiente :)

evite adicionar o mesmo hash simultâneo ao seu roteador e cuide também da condição inicial, vou te mostrar um código
(para lidar com o caso inicial) (para evitar adicionar as mesmas rotas simultâneas)
if ( route.length === 1 || routes[routes.length-1] === getHash() ) {
retorna falso;
}
outro{
rotas.push(getHash())
};

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

etler picture etler  ·  13Comentários

omenking picture omenking  ·  10Comentários

rafde picture rafde  ·  9Comentários

jamiebuilds picture jamiebuilds  ·  12Comentários

miguelpayet picture miguelpayet  ·  9Comentários