2011-12-22 15 views
17

Todos sabemos hacer algo como esto es malo:Delegación de eventos a los sub-vistas en Backbone.js

<ul> 
    <li>Item</li> 
    <li>Item</li> 
    ... 500 more list items 
</ul> 

y luego ...

$("ul li").bind("click", function() { ... }); 

que he estado buscando por mucho de ejemplos/guías de Backbone y el siguiente parece ser un enfoque estándar para representar una lista con elementos, basados ​​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 lista de elementos:

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, una instancia de la vista de lista con una colección de 500 modelos, me da 500 eventos de clic, uno para cada fila. Esto está mal ¿no?

Sé Backbone ha construido en delegación de eventos para eventos de espacios de nombres:

events : { 
    'click li' : 'log' 
} 

supongo que podría poner esto en mi ListView, y sólo crearía un evento de clic de toda la lista, pero luego me wouldn no se puede acceder a los datos del modelo correspondiente al elemento de la lista cliqueada.

¿Qué patrones usan los desarrolladores de backbone para resolver este problema típico?

Respuesta

2

Puede asociar la instancia con un elemento de este modo:

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

continuación:

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; 
    } 
}); 
+1

Gracias por su respuesta. Esto es una especie de lo que he tenido que hacer para solucionar este problema hasta ahora, pero de nuevo, usar jQuery para almacenar datos en el DOM, no parece un enfoque muy limpio a largo plazo. – Daniel

+1

@Daniel Well jQuery no almacena los datos en el DOM, lo almacena en un objeto js regular ('jQuery.cache'). jQuery también contamina internamente cualquier elemento que tenga evento (s) con una propiedad de expansión de todos modos, después de lo cual hacer 'datos' en el elemento no tiene otro efecto que agregar más propiedades en el objeto js regular. – Esailija

+0

Ah Ok. Bueno, supongo que eso funcionaría entonces. gracias – Daniel

3

Mantenga un registro de las subvistas desde la vista principal. Luego, al agregar la subvista, agréguelo al hash y agregue el cid al el de la subvista. De esta manera tiene 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 mal 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; 
    } 
}); 
Cuestiones relacionadas