2011-12-22 6 views
17

Nous savons tous faire quelque chose comme cela est mauvais:événements Déléguer à des sous-vues Backbone.js

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

puis ...

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

J'ai cherché par beaucoup de Backbone exemples/guides et ce qui suit semble être une approche standard pour le rendu d'une liste avec des éléments, à partir d'une collection de modèles.

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

Une vue d'élément de liste:

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 je ne me trompe pas, l'instanciation listView avec une collection de 500 modèles, me donne 500 événements de clic, un pour chaque ligne. C'est mauvais, non?

Je sais que Backbone a construit en délégation pour les événements namespaced:

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

Je suppose que je pourrais mettre dans mon ListView, et il ne ferait que créer un événement de clic pour toute la liste, mais je ne serais Ne pas pouvoir accéder aux données du modèle correspondant à l'élément de la liste cliquée.

Quels sont les modèles utilisés par les développeurs de backbone pour résoudre ce problème type?

Répondre

2

Vous pouvez associer l'instance avec un élément comme ceci:

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

Puis:

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

Merci pour votre réponse. C'est une sorte de ce que j'ai dû faire pour contourner ce problème jusqu'à présent, mais encore une fois, Il utilise jQuery pour stocker des données dans le DOM, ne se sent pas comme une approche très propre à long terme. – Daniel

+1

@Daniel Bien jQuery ne stocke pas les données dans le DOM, il les stocke dans un objet js régulier ('jQuery.cache'). jQuery pollue aussi de façon interne tout élément ayant un ou plusieurs événement (s) avec une propriété expando, après quoi faire 'data' sur l'élément n'a d'autre effet que d'ajouter plus de propriétés dans l'objet js normal. – Esailija

+0

Ah Ok. Eh bien, je suppose que cela fonctionnerait alors. merci – Daniel

3

Conserve les sous-vues de la vue parent. Puis, lors de l'ajout de la sous-vue, ajoutez-la au hachage et ajoutez le cid à ​​l'élément de la sous-vue. De cette façon ont un pointeur vers la sous-vue et peuvent effectuer des opérations sur son modèle etc ...

Je n'ai pas testé ce code exact ci-dessous donc CECI peut être faux dans un endroit ou deux mais j'ai testé ce principe général. J'ai également omis le code 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; 
    } 
}); 
Questions connexes