javascript - Delegar eventos a sub-vistas en Backbone.js
performance (3)
Todos sabemos que hacer algo como esto es malo:
<ul>
<li>Item</li>
<li>Item</li>
... 500 more list items
</ul>
y entonces...
$("ul li").bind("click", function() { ... });
He estado revisando muchos ejemplos / guías de Backbone y el siguiente parece ser un enfoque estándar para generar una lista con elementos, basada en una colección de modelos.
var ListView = Backbone.View.extend() {
tagName: ''ul'',
render: function() {
this.collection.each(function(item) {
var view = new ListItemView({model: item});
$(this.el).append(view.render().el);
});
return this;
}
});
Una vista de elemento de lista:
var ListItemView = Backbone.View.extend() {
tagName: ''li'',
events: {
''click'' : ''log''
}
log : function() {
console.log(this.model.get("title"));
}
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
Si no me equivoco, crear una instancia de listView con una colección con 500 modelos, me da 500 eventos de clic, uno para cada fila. Esto es malo verdad?
Sé que Backbone ha incorporado la delegación de eventos para eventos con espacios de nombre:
events : {
''click li'' : ''log''
}
Supongo que podría poner esto en mi ListView, y solo crearía un evento de clic para toda la lista, pero luego no podría acceder a los datos del modelo correspondientes al elemento de la lista en la que se hizo clic.
¿Qué patrones usan los desarrolladores de red troncal para resolver este problema típico?
Derick Bailey escribió una publicación de blog detallada sobre este dilema, puede consultarla aquí: http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
Mantenga un registro de las subvistas de la vista principal. Luego, al agregar la subvista, agréguela al hash y añada el cid al el de la subvista. De esta manera, tenga un puntero a la subvista y podría realizar operaciones en su modelo, etc.
No he probado este código exacto a continuación, por lo que ESTO puede estar equivocado en un lugar o dos, pero he probado este principio general. También he omitido el código listitemview.
var ListView = Backbone.View.extend() {
subViews: {},
tagName: ''ul'',
events: {
''click li'' : ''clickItem''
},
clickItem: function(event){
var id = event.currentTarget.cid;
var subView = this.subViews[id];
},
render: function() {
this.collection.each(function(item) {
var view = new ListItemView({model: item});
this.subViews[view.cid] = view;
subEl = view.render().el;
subEl.cid = view.cid;
$(this.el).append(subEl);
});
return this;
}
});
Puedes asociar la instancia con un elemento así:
events : {
''click li'' : ''log''
},
log: function( e ) {
var elm = e.currentTarget //Same as `this` in normally bound jQuery event
jQuery.data( elm, "viewInstance" ).log( e );
},
Entonces:
var ListItemView = Backbone.View.extend() {
tagName: ''li'',
log : function() {
console.log(this.model.get("title");
}
render: function() {
//Associate the element with the instance
$(this.el).html(this.template(this.model.toJSON())).data( "viewInstance", this );
return this;
}
});