Reputation: 827
I'm learning Marionette.js and have a scenario, where my app has:
app.addRegions({
main: '#omen',
newItem: '#addnewitem',
counter: '#counter'
});
These regions. I have these Model/Collections:
var Item = Backbone.Model.extend(),
Items = Backbone.Collection.extend({
model: Item,
url: 'api/items'
}),
I have an Item view and Items view:
ItemView = Mn.ItemView.extend({
tagName: 'tr',
template: '#itemView',
events: {
'click #btnDeleteBook' : 'deleteItem'
},
deleteItem: function() {
app.trigger('item:delete', this.model);
}
}),
ItemsView = Mn.CollectionView.extend({
tagName: 'table',
childView: ItemView,
onShow: function(view) {
TweenMax.staggerFrom($(view).find('td'), 1, {
x: 100
}, 2);
}
}),
I have an initializer function, that listens for events above and does stuff through app.ItemController. It all works fine.
But now I want to add a region (counter region), that displays the total number of items in my collection. I need this to be a separate view ideally, because I will be displaying it in different places.
So I do this:
DisplayCounter = Mn.ItemView.extend({
template: _.template('Total: '+ app.Items.length),
}),
app.Items
is an instance of Collection
declared above. But even before instantiation of DisplayCounter
, I get error:
Uncaught TypeError: Cannot read property 'length' of undefined.
Please help... :(
------------------------- E D I T ----------------------
I've achieved it, but it seems to be so complicated to do such a tiny thing.
Changed my collection like so:
Items = Backbone.Collection.extend({
model: Item,
url: 'api/items',
initialize: function() {
this.listenTo(this, 'add', function() {
app.trigger('collection:updated', this);
});
}
}),
and changed my DisplayCounter like this:
DisplayCounter = Mn.ItemView.extend({
template: _.template('Total: <%=length%>'),
templateHelpers: function() {
return {
length: this.lengthVariable || 0
}
},
initialize: function() {
app.on('collection:updated', function(params){
this.lengthVariable = params.length;
this.render();
}.bind(this));
}
}),
I can't believe there's no easier way to do this.. :/
Upvotes: 0
Views: 1134
Reputation: 2776
app.Items is not being defined.
In Marionette you can define which collection or model are your views going to use.
ItemsView = Mn.CollectionView.extend({
tagName: 'table',
childView: ItemView,
collection: myItems // An instance of your collection
onShow: function(view) {
TweenMax.staggerFrom($(view).find('td'), 1, {
x: 100
}, 2);
}
}),
So marionette is going to render one itemView per element in your collection. Then inside of your collection view this.collection
is going to refer to the collection instance. So this.collection.length
will have what you need.
And in your ItemView
you can get the corresponding model by using this.model
Upvotes: 0
Reputation: 30330
The code that sets up DisplayCounter
is being run before the code that puts an instance of Items
into app.Items
.
Even if you avoided this problem by assigning app.Items
first, you'd still have a problem - the template
property is only set once so you'd only ever see the length of app.Items
at the time that you define DisplayCounter
.
Rather than hard-coding the value directly into the template string, you should supply a value at render time. Mn.View.serializeData
allows you to customise the data that is passed into the template function at render time:
DisplayCounter = Mn.ItemView.extend({
template: _.template('Total:: <%= itemCount %>),
serializeData: function() {
return { itemCount: app.Items.length }
}
}),
Upvotes: 1