I have the following code that happens after a collection sync:
adGeneration: function() {
var child = this.children.findByIndex(this.children.length - 1);
console.log(child.model.toJSON());
eventer.trigger('generate:new:ad', child);
},
The problem I am running into, is that, after the first sync, the child
element is a blank model:
First Time:
Object {id: "5-vp39kv3uiigxecdi", size: 26, price: "9.84", face: "( ⚆ _ ⚆ )"}
Every time after:
Object {length: 40, models: Array[41], _byId: Object, _listeningTo: Object, _listenId: "l14"…}
ProductsCollection
define(["backbone", "lodash", "fonts/products/model", "eventer"],
function(Backbone, _, ProductModel, eventer) {
'use strict';
var ProductsCollection = Backbone.Collection.extend({
model: ProductModel,
sort_key: 'price',
url: '/api/products',
offset: 0,
initialize: function() {
this.listenTo(eventer, 'fetch:more:products', this.loadMore, this);
},
comparator: function(item) {
return item.get(this.sort_key);
},
sortByKey: function(field) {
this.sort_key = field;
this.sort();
},
parse: function(data) {
return _.chain(data)
.filter(function(item) {
if(item.id) {
return item;
}
})
.map(function(item){
item.price = this.formatCurrency(item.price);
return item;
}.bind(this))
.value();
},
formatCurrency: function(total) {
return (total/100).toFixed(2);
},
loadMore: function() {
this.offset += 1;
this.fetch({
data: {
limit: 20,
skip: this.offset
},
remove: false,
success: function(collection) {
this.add(collection);
}.bind(this)
});
}
});
return ProductsCollection;
});
LayoutView that contains the View for the productions collection. The collection is fetched onShow of the layoutview
define(["marionette", "lodash", "text!fonts/template.html",
"fonts/controls/view", "fonts/products/view", "fonts/products/collection", "eventer"],
function(Marionette, _, templateHTML, ControlsView, ProductsView,
ProductsCollection, eventer) {
'use strict';
var FontsView = Marionette.LayoutView.extend({
regions: {
controls: '#controls',
products: '#products-list'
},
template: _.template(templateHTML),
initialize: function() {
this._controlsView = new ControlsView();
this._productsView = new ProductsView({
collection: new ProductsCollection({
reorderOnSort: false,
sort: false
})
});
this.listenTo(this._productsView.collection, 'sync', this.loading, this);
this.listenTo(eventer, 'fetch:more:products', this.loading, this);
this.listenTo(eventer, 'products:end', this.productsEnd, this);
},
onRender: function() {
this.getRegion('controls').show(this._controlsView);
this.getRegion('products').show(this._productsView);
this.loading();
},
onShow: function() {
this._productsView.collection.fetch({
data: {
limit: 20
}
})
},
productsEnd: function() {
this.loading();
this.$el.find('#loading').html("~ end of catalogue ~")
},
loading: function() {
var toggle = this.$el.find('#loading').is(':hidden');
this.$el.find('#loading').toggle(toggle);
}
});
return FontsView;
});
AdsView:
define(["marionette", "lodash", "text!ads/template.html", "eventer"],
function(Marionette, _, templateHTML, eventer) {
'use strict';
var AdsView = Marionette.ItemView.extend({
template: _.template(templateHTML),
ui: {
ad: '.ad'
},
initialize: function() {
this.listenTo(eventer, 'generate:new:ad', this.generateNewAd, this);
},
onShow: function() {
// Set add image onShow
this.ui.ad.prop('src', '/ad/' + this.randomNumber());
},
generateNewAd: function(childView) {
var newAd = this.ui.ad.clone(),
element = childView.$el,
elementId = childView.model.get("id");
newAd.prop('src', '/ad/' + this.randomNumber());
$("#" + elementId).after(newAd);
},
randomNumber: function() {
return Math.floor(Math.random()*1000);
},
setUpAd: function() {
this.ui.ad.prop('src', '/ad/' + this.randomNumber());
}
});
return AdsView;
});
I think your problem is in the
ProductsCollection.loadMore
method. There, in thesuccess
callback to yourfetch
you do,What's happening behind the scenes is that before your
success
callback is invoked, Backbone will first runCollection.set()
on your data. By default, insideset
your data will be parsed into a array of models returned byProductsCollection.parse
and if any new models are found they will beadd
'ed to your existing collection (note that unless you pass{ remove: false }
to yourfetch
options, models in your collection which are not in your last fetch will be removed. SeeCollection.set
)So, what happens when you do the
fetch
inloadMore
, which is called after the firstfetch
, Backbone will firstadd
all the models from the server (that are returned fromProductsCollection.parse
) and then invoke thesuccess
callback of yourfetch
, which, essentially does one lastadd
. And what it'sadd
'ing is theProductsCollection
instance. Notcollection.models
, an array of models, rather a Backbone object that has a propertymodels
that holds a raw array of models. Hence, the strange output:Simply remove that
success
callback (which is unnecessary) and the last child view of yourProductsView
should be the view rendered from the last model returned.