Reputation: 475
I have a question, really basic stuff I think but:
I've only seen examples with a collection view and single view dependent on a collection being updated. What if you have multiple views trying to subscribe to a collections events, ie reset, addOne, addAll etc...
Am I missing some point about doing/not doing this? Do you have any examples of this? Does that even make sense?
Any info is MUCH appreciated
var Coll = Backbone.Collection.extend({
model: SingleModel,
url: 'service',
initialize: function(){
console.log('collection inited')
}
});
var SingleModel = Backbone.Collection.extend({});
var CollView = Backbone.View.extend({
el: 'ul',
template: Handlebars.compile(someContainerTemplate),
init: function(){
_.bindAll(this, 'render', 'addAll', 'addOne');
this.collection.bind("reset", this.addAll);
this.collection.fetch();
},
render: function(){
$(this.el).append(this.template())
},
addAll: function(){
this.collection.each(this.addOne);
},
addOne: function(model){
var view = new SingleView({ model: model })
}
})
var SingleView = Backbone.View.extend({
tagName: "li",
events: {
"click .delete": "remove"
},
template: Handlebars.compile(someTemplateForSingleItem),
initialize: function() {
_.bindAll(this,'render');
this.model.bind('save', this.addOne);
this.model.bind('destroy', removeEl);
},
remove: function(){
this.model.destroy();
},
removeEl: function(){
$(this.el).remove();
},
render: function() {
var context = this.model.toJSON();
return $(this.el).append(this.template(context));
},
})
// standard so far (excluding any bad practices),
// but what if you have another view dependent on
// say the number of length of the collection, and
// you want it to update if any single models are destroyed
var HeaderView = Backbone.View.extend({
tagName: "div#header",
template: Handlebars.compile(someHeaderTemplate),
initialize: function() {
_.bindAll(this,'render');
this.model.bind('save', this.addOne);
},
render: function() {
//assigning this collection length
var context = this.collection.length;
return $(this.el).append(this.template(context));
},
});
var coll = new Coll();
new CollView({ collection: coll });
new HeaderView({ collection: coll});
Upvotes: 16
Views: 8979
Reputation: 434635
What you're doing is perfectly fine and part of the reason for using Backbone. From the Backbone introduction:
Whenever a UI action causes an attribute of a model to change, the model triggers a "change" event; all the Views that display the model's state can be notified of the change,
Note that they say "all the Views", not "the view".
One example of multiple views for a single collection would be a chat system. Suppose you have a collection of users that are online; then you might have one simple view in the header that displays the number of people that are online and another view (of the same collection) that lists the users:
var User = Backbone.Model.extend({});
var OnlineUsers = Backbone.Collection.extend({
model: User
});
var CounterView = Backbone.View.extend({
tagName: 'span',
initialize: function() {
_.bindAll(this, 'render');
this.collection.on('add', this.render);
// other interesting events...
},
render: function() {
this.$el.text(this.collection.size());
return this;
}
});
var ListView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.collection.on('add', this.render);
// other interesting events...
},
render: function() {
var html = this.collection.map(function(m) {
return '<p>' + m.get('name') + '</p>';
});
this.$el.html(html.join(''));
return this;
}
});
Then you'd have one OnlineUsers
instance but both your CounterView
and ListView
instances would be watching it. When people come online or go offline, both views will be updated as desired.
Simple demo: http://jsfiddle.net/ambiguous/eX7gZ/
The above example situation sounds like exactly the sort of thing you're doing and it is exactly the sort of thing that Backbone is for. Good job.
Upvotes: 30
Reputation: 45
We had the same problem. We needed to use multiple jQuery Templates with unique internal events for each model without using multiple holder views. This is the solution that we came up with:
var myHolderView = Backbone.View.extend({
el: '#views',
render: function(){
// This is because 'this' change inside the collection.each
var $this = this;
// If you want a wrapper template
var wrapperHtml = $('#view-wrapper-template').tmpl();
$this.$el.append(wrapperHtml);
$wrapper = $this.$el.find('> div'); // If wrapper is a div
$this.collection.each(function(model){
// Render and append the viewA with internal events
var viewA = new myViewA({model: model});
viewA.render();
// Use this to add the views content (viewA.el) to this views element ('#views')
//$this.$el.append(viewA.el);
// Use this to add the view content (viewA.el) to this views wrapper element ($wrapper)
$wrapper.append(viewA.el);
// Render and append the viewB with internal events
var viewB = new myViewB({model: model});
viewB.render();
//$this.$el.append(viewB.el);
$wrapper.append(viewB.el);
// Add more views here...
});
}
});
Full source and working example: http://jsfiddle.net/HLv5z/9/
Upvotes: 2