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>
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 ().