Backbone: duplicar eventos ao instanciar a visualização duas vezes

Criado em 5 fev. 2012  ·  9Comentários  ·  Fonte: jashkenas/backbone

Quando defino uma visualização (via Backbone.View.extend) e a instancio duas vezes (via new), quando um evento ocorre uma vez em cada objeto ivies, ele é disparado duas vezes.

Pequeno exemplo:

<html>
<head>
    <script type="text/javascript" src="js/jquery-1.7.1.js"></script>
    <script type="text/javascript" src="js/underscore-1.3.1.js"></script>
    <script type="text/javascript" src="js/backbone-0.9.1.js"></script>
</head>
<body>
    <div id="test"></div>
    <script type="text/javascript">
        testview = Backbone.View.extend ({
            el: '#test',
             events: {'click .test': 'doTest'},
             doTest: function() {alert('test')},
             render: function() {$('#test').append($('<a>').attr('href', '#').addClass('test').append('test'));}});
        view_one = new testview;
        view_two = new testview;
        view_one.render();
    </script>
    </body>
</html>

Todos 9 comentários

Por curiosidade, por que você está instanciando-o duas vezes? Você está em uma visualização móvel ou algo em que a primeira instância na janela de visualização é substituída por outra visualização, e quando o usuário volta para a primeira visualização, você está recebendo ligações duplas?

Algo parecido. Eu tenho uma visão inicial, que irá chamar terminais diferentes se o usuário estiver conectado ou não. As ligações de eventos são para os botões de login / logout. Ambas as visualizações são instanciadas, mas apenas uma é renderizada por vez. Quando eles fizerem login ou logout, alternarei para a mesma visualização, mas em um modo diferente. Espero que esteja claro o suficiente.

Ainda não está totalmente claro para mim, por que você tem ambas as visualizações instanciadas ao mesmo tempo. Ter ambos vinculados ao mesmo elemento pré-existente no dom significa que haverá eventos duplos. Este não é realmente um bug, mas apenas a maneira como os eventos e, especificamente, a delegação de eventos funciona.

Se você instanciar uma visualização em um elemento pré-existente, você precisa desvincular especificamente esses eventos ou eles persistirão até que o nó do próprio elemento seja removido do dom.

Bem, parecia uma boa ideia instanciar ambas as visualizações durante a inicialização, vincular ambos ao mesmo elemento raiz no DOM e, em seguida, renderizar um ou outro de acordo com o modo de login e fazer com que substituísse os elementos do outro durante a renderização. Acho que há uma maneira melhor de fazer isso, porém, a partir de seus comentários.

Gostaria de instanciar visualizações quando você precisar delas. Se um modo de exibição não estiver sendo usado, não há necessidade de usá-lo para ocupar memória e, potencialmente, introduzir efeitos colaterais como os que você está vendo.

Eu tendo a não usar este padrão:

var View = Backbone.View.extend ({
    el: '#test',
    events: 
    {
        'click .test': 'onClick'
    },
    onClick: function(event) 
    { 
        ... 
    }
});

view = new View;
view.render();

mas, em vez disso, tendo a usar esse padrão, exatamente para evitar visualizações zumbis que resultam em duplicação de eventos

var View = Backbone.View.extend ({
    events: 
    {
        'click .test': 'onClick'
    },
    onClick: function(event) 
    { 
        ... 
    }
});

view = new View;
$('#target').append(view.render().el);

O único local onde eu usaria seu primeiro padrão é na visualização do Chrome do aplicativo que instancia a GUI:

var Application = Backbone.View.extend({
    el: '#wrapper',
    initialize: function (options)
    {
        this.render();
    }
)};

$(document).ready(function () {
    window.application = new Application();
});

idem sobre o que @vincentbriglia disse sobre não definir el e deixar a visão gerar seu próprio elemento. Eu também faço isso 95% das vezes. Se, no entanto, você deseja definir el e limpar os eventos ao remover, você pode definir seu próprio método Backbone.View.destroy() com um .off() para cada on() e todos os eventos em events: {}

por exemplo:

var MyView = Backbone.View.extend({

  initialize: function() {
    this.model.on('change', this.render, this);
  },

  render: function() {
    return this;
  },

  destroy: function() {
    this.remove();
    this.model.off('change', this.render, this);
  }
})

Obrigado, @malandrew e @vincentbriglia , isso realmente ajuda.

Só para atualizar isso para a posteridade ... Eu adicionei um método destroy à classe View que se parece com este

           destroy: function() {
                this.undelegateEvents();
            }

Então, apenas destruo a visão atual (State.view) sempre que instanciar ().

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