Reputation: 63599
I have a Collection App.listingList
where subsequent fetch()
are called with add:true
.
App.listingList.fetch({
data: {some:data},
processData: true,
add: true
});
Problem: How can the newly added models have their views rendered, without re-rendering the views of the existing models. This means I cannot do:
this.collection.each( function(listing, index) {
new ListingMarkerView({ model:listing }).render();
}, this);
Attempt #1
Rendering the View on the collection's add
event, I cannot figure out a way to access the new models to render
ListingListView = Backbone.View.extend({
initialize: function() {
this.collection.bind('add', this.renderNew, this);
},
render: function() {
console.log('render ListingListView');
this.collection.each( function(listing, index) {
new ListingMarkerView({ model:listing }).render();
}, this);
return this;
},
renderNew: function() {
// How do I grab the new models?
new ListingMarkerView({ model:listing }).render(); // wont work
return this;
}
});
Attempt #2
I tried having a second Collection to do the subsequent fetch
on, and compare the models of both collections using underscore.js's _.without()
, but the array returned still contains the elements found in the 2nd array passed as the parameter. Using _difference()
also returned the same array passed as the first array.
App.listingListNew.fetch({
data: {some:data},
processData: true,
success: function() {
console.log(App.listingListNew.models);
console.log(App.listingList.models);
console.log(_.without(App.listingListNew.models, App.listingList.models));
console.log(_.difference(App.listingListNew.models, App.listingList.models));
}
});
console.log
Output
Since I passed in 2 identical arrays into _.difference()
and _.without()
, the output should be []
. But it isnt :/ Maybe because cid
is different, so every one of them are treated as unique?
Upvotes: 5
Views: 2879
Reputation: 994
I know this is an old question, but I was having the same issue and came across this response so I thought I'd add an alternate approach. I'm not sure how efficient it is, but it works. I'm using this to support an infinite scroll feature.
I'm using Backbone 1.2.1, so in the collection fetch, I'm using remove:false
instead of the deprecated add:true
per the docs here: http://backbonejs.org/#Collection-fetch
The basic approach is to set a rendered
attribute to true
on each item in the collection when first rendered, then use that to ignore previously rendered items on subsequent fetches.
Model and Collection:
MyApp.Item = Backbone.Model.extend({});
MyApp.ItemList = Backbone.Collection.extend({
model: MyApp.Item,
url: '/api/item/',
parse : function(response){
if (response.stat) {
return _.map(response.content, function(model, id) {
model.id = id;
return model;
});
}
}
});
Views:
MyApp.ItemListView = Backbone.View.extend({
tagName: 'ul',
className: 'item-list',
render: function() {
this.collection.each(function(item){
//don't render items that have already been rendered!
if (!item.rendered) {
var itemListDetailView = new MyApp.ItemListDetailView({model: item});
this.$el.append(itemListDetailView.render().el);
item.rendered = true;
}
}, this)
return this;
}
});
MyApp.ItemListDetailView = Backbone.View.extend({
tagName: 'li',
className: 'item-list-detail',
render: function() {
$(this.el).html( '<div class="item-title">' + this.model.get('title') + '</div>');
return this;
}
});
Fetch function:
MyApp.loadMyItems = function () {
MyApp.gettingData = true; //flag for infinite scroll
MyApp.myItems.fetch({
traditional: true,
remove:false,
data: {
u_id: MyApp.User.id,
order: 'create_date:desc',
start: MyApp.myItems.length,
num_items: 10
},
success: function(){
MyApp.gettingData = false; //flag for infinite scroll
MyApp.myItemsView.render();
}
});
};
Calling:
//on initial page load
MyApp.myItems = new MyApp.ItemsCollection();
MyApp.myItemsView = new MyApp.ItemListView({
collection: MyApp.myItems,
el: $('#my-items')
});
MyApp.loadMyItems();
//infinite scroll
$('#items .infinite-scroll').on('loadmore', function () {
if (!MyApp.gettingData) {
MyApp.loadMyItems();
}
});
Upvotes: 0
Reputation: 8581
When you do a collection.bind('add', this.renderNew, this);
it automatically passes the added model to your method as an argument.
Include the argument in your method and you should have access to the new model.
renderNew: function(newModel) {
new ListingMarkerView({ model:newModel }).render();
return this;
}
Upvotes: 5