I have an issue to access my collection on a view's method, in fact, it works well from the initialize() method, but i created another one (drawVisualization()), and i get an undefined error when i try to access this.collection, it's probably a stupid question but i didn't find any solution, i tried to use _.bind on the initialize method but doesn't seem works in this case, here is the code:
App.Views.account = Backbone.View.extend({
className: 'account',
el: $('#account-container'),
initialize: function(){
console.log(this.collection.toJSON()); //Works fine !
this.template = _.template($('#account-template').html());
_.bind(this.drawVisualization, this); //Seems to be useless
},
render: function(){
//Some code...
return this;
},
drawVisualization: function(){
console.log(this.collection.toJSON()); //Fail because of undefined collection !
}
Thanks for your help !
I don't have an idea why it doesn't work, but try using underscore's bindAll.
initialize: function(){
_.bindAll(this);
this.template = _.template($('#account-template').html());
}
For me just dumping bindAll in the beginning of each view initialize is a good way of avoiding this kind of problems.
In backbone you don't need to bind this to a backbone view's method.
So try skipping this part
_.bind(this.drawVisualization, this); //Seems to be useless
and instead bind this like this:
this.collection.bind("reset", this.drawVisualization, this);
Related
When an ItemView is listening to a model other than its model (this.model), do I simply need to turn off listeners within the remove function? And set their reference to null? I wonder if the ItemView will be safely destroyed, or if I'll be in trouble later when tons of views like this will be created / destroyed?
Example :
var FriendListItemView = Marionette.ItemView.extend({
[...]
initialize: function(){
Marionette.ItemView.prototype.initialize.apply(this, arguments);
// get the friend and the user from global "users" collection
this.user = users.get(this.model.get('user_id'));
this.friend = users.get(this.model.get('friend_id'));
this.user.on('change:name', this.render, this);
this.friend.on('change:name', this.render, this);
},
remove: function(){
this.user.off('change:name', this.render, this);
this.friend.off('change:name', this.render, this);
this.user = null;
this.friend = null;
Marionette.ItemView.prototype.remove.apply(this, arguments);
},
});
Instead of using this.user.on('change:name', this.render, this); use the listenTo() function.
this.listenTo(this.user, 'change:name', this.render);
Marionette will in its default destroy call the remove method from Backbone, which again will call stopListening and clear out all event listeners registered via listenTo. That should make your entire remove-function unnessescary. These kind of things is one of the problems that Marionette is meant to take care of for you. Setting this.user and this.friends to null is also unnessescary.
just to clarify a bit further. The destroy method will also trigger events and call related methods. Additionally ItemView's initialize is a noop, so no reason to call the prototype. Most everything has before and after event hooks so you don't need to call the prototype.
var FriendListItemView = Marionette.ItemView.extend({
[...]
initialize: function(){
// get the friend and the user from global "users" collection
this.user = users.get(this.model.get('user_id'));
this.friend = users.get(this.model.get('friend_id'));
this.listenTo(this.user, 'change:name' this.render);
this.listenTo(this.friend, 'change:name' this.render);
},
onDestroy: function(){
// you could do additional clean up here if you needed to
// but you don't. There's also an onBeforeDestroy
// equivalent to this.on('destroy', this.doSomething);
},
});
I am following CodeSchool's 'Anatomy of Backbone.js' and cannot get this to work on my machine. There are similar questions, but they have a lot of extra stuff going on and, for someone brand-new like me, it's making it hard to learn.
Here's the code as simple/universal as possible:
var WorldEvent = Backbone.Model.extend({});
var WorldEventView = Backbone.View.extend({
events: {
'click' : 'focusEvent'
},
focusEvent: function(){
alert('great.');
},
className : 'pin bounce',
render : function () {
console.log('did something');
this.$el.html("rendered");
return this;
}
});
var WorldEventCollection = Backbone.Collection.extend({
model: WorldEvent,
url: '/events'
});
var worldEventCollection = new WorldEventCollection();
var worldEventCollectionView = new WorldEventView({
collection: worldEventCollection,
initialize: function(){
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.addAll, this);
},
addOne: function(myEvent){
var worldEventView = new WorldEventView({ model: myEvent });
this.$el.append(worldEventView.render().el);
},
addAll: function(){
this.collection.forEach(this.addOne, this);
},
render: function(){
this.addAll();
}
});
The good news is that if I call
worldEventCollection.add(new WorldEvent( {<my data>} ));
... the new model is added to worldEventCollection - I've logged worldEventCollection and worldEventCollection.length to verify.
The bad news is that "did something" doesn't appear in the console and I see no evidence of a render.
Please help, I've wasted a ton of time on what is probably super simple. Thank you.
UPDATE
Okay, I found one of my issues. I needed to define a separate WorldEventCollectionView class altogether, so this was NOT correct:
var worldEventCollectionView = new WorldEventView({
collection: worldEventCollection,
...
Instead, I believe one correct approach is:
var WorldEventCollectionView = Backbone.View.extend({
initialize: function(){
this.collection.on('add', this.addOne, this.collection);
...
And then:
var worldEventCollectionView = new WorldEventCollectionView({ collection: worldEventCollection });
WorldEventView.render must end with return this; as per backbone view convention, otherwise chaining such as worldEventView.render().el will not work. Specifically, that will throw an exception since render() returns undefined and you try to access the .el property of undefined.
There's several other things that are not quite right about your snippet as well, but fix that first and see if you can take it from there. Generally in a view's render method, you want to populate HTML inside the view's this.$el and return this; at the end of render and really that's all you should be doing. Render has a very specific purpose and code that isn't following that basic idea and semantic belongs elsewhere.
Oh so this:
var worldEventCollectionView = new WorldEventView({
should be:
var WorldEventCollectionView = Backbone.View.extend({
My code is below, when I run it, it shows error - <HTMLAudioElement> has no method 'set'.
Why it''s happening? I've bound my functions to model, but it seems to work wrongly:
var Player = Backbone.Model.extend({
initialize: function(){
_.bind(this.ontimeupdate, this);
_.bind(this.onprogress, this);
},
setAudio: function(ogglink, mp3link, ontimeupdate){
var el = document.createElement("audio");
el.addEventListener('timeupdate', this.ontimeupdate);
this.audiotag = el;
},
ontimeupdate: function() {
this.set("curtime", this.currentTime);
}
});
Unlike bindAll (which you should be using as you make several binds), bind only returns the binded function (equivalent to ECMA's bind method). So...
this.ontimeupdate = _.bind(this.ontimeupdate, this);
this.onprogress = _.bind(this.onprogress, this);
Or
_.bindAll(this);
Or
_.bindAll(this, 'ontimeupdate', 'onprogress');
If you're binding those methods to this (your Player instance) then your ontimeupdate method should probably look more like this:
ontimeupdate: function() {
this.set("curtime", this.audiotag.currentTime);
}
I'm using Handlebars template engine.
so, I have Model:
Backbone.Model.extend({
urlRoot: Config.urls.getClient,
defaults: {
contract:"",
contractDate:"",
companyTitle:"",
contacts:[],
tariff: new Tariff(),
tariffs: [],
remain:0,
licenses:0,
edo:""
},
initialize:function(){
this.fetch();
}
});
then Marionette ItemView:
Marionette.ItemView.extend({
template : templates.client,
initialize: function () {
this.model.on('change', this.render, this);
},
onRender: function () {
console.log(this.model.toJSON());
}
});
and then I call everything as:
new View({
model : new Model({id:id})
})
and, it's immediately render a view for me and this is cool.
But after the model fetched data it's trigger "change", so I see in console serialised model twice, and I see for first time empty model and then filled one.
But, the view is NOT updated.
How I can fix it?
P.S. I understand, that I can call a render method on fetch done callback. But I also need it for further actions, when user will change model.
In the View, You can use following code
modelEvents: {
'change': 'render'
}
instead of
initialize: function () {
this.model.on('change', this.render, this);
},
onRender: function () {
console.log(this.model.toJSON());
}
Actually, Backbone and Marionette are smart enough to do it.
Problem was in template and data as I found it another question. So, I re-checked everything and got result.
I'm having a look at Backbone.js, but I'm stuck. The code until now is as simple as is possible, but I seem not to get it. I use Firebug and this.moments in the render of MomentsView is an object, but all the methods from a collection don't work (ie this.moments.get(1) doesn't work).
The code:
var Moment = Backbone.Model.extend({
});
var Moments = Backbone.Collection.extend({
model: Moment,
url: 'moments',
initialize: function() {
this.fetch();
}
});
var MomentsView = Backbone.View.extend({
el: $('body'),
initialize: function() {
_.bindAll(this, 'render');
this.moments = new Moments();
},
render: function() {
_.each(this.moments, function(moment) {
console.log(moment.get('id'));
});
return this;
}
})
var momentsview = new MomentsView();
momentsview.render();
The (dummy) response from te server:
[{"id":"1","title":"this is the moment","description":"another descr","day":"12"},{"id":"2","title":"this is the mament","description":"onother dascr","day":"14"}]
The object has two models according to the DOM in Firebug, but the methods do not work. Does anybode have an idea how to get the collection to work in the view?
The problem here is that you're fetching the data asynchronously when you initialize the MomentsView view, but you're calling momentsview.render() synchronously, right away. The data you're expecting hasn't come back from the server yet, so you'll run into problems. I believe this will work if you call render in a callback to be executed once fetch() is complete.
Also, I don't think you can call _.each(this.moments) - to iterate over a collection, use this.moments.each().
Try removing the '()' when instantiate the collection.
this.moments = new Moments;
Also, as it's an asynchronous call, bind the collection's 'change' event with the rendering.
I hope it helps you.