Object doesn't support property or method 'bind' - Backbone.js - backbone.js

I have build my application in Backbone.js using MVC. Everything, is running fine in Chrome/Firefox/IE 9 and above but not in IE8 and below:-
var userDetailView = backbone.View.extend({
el: '#user-details',
tagName: 'div',
template: Handlebars.templates.UserDetails,
model: userModel,
initialize: function () {
_.bindAll(this, "render");
this.model.bind('change', this.render);
return this;
}
});
I am getting error as below:-
SCRIPT438: Object doesn't support property or method 'bind'
Can anyone help?

What is this.model? How are you instantiating that view? I'd guess that this.model is not what it should be. In a view, the model property should be a model instance and you're suppose to say things like:
var m = new Model;
var v = new View({ model: m });
A model instance will have a bind method (AKA on) but, in non-stone-age browser, so will a function:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
A model "class":
var M = Backbone.Model.extend({ ... });
is a function (just like anything else you can call new on in JavaScript). This means that you can say things like this in newer browsers:
var M = Backbone.Model.extend({ ... });
var v = new View({ model: M });
and this.model.bind('change', this.render) will execute inside the View, it won't call the bind you're looking for but it will call a bind, it will call Function.bind.
Start passing a model instance to your view and things should start making more sense.
Clarification: If you check the MDN Browser compatibility section on Function.bind, you'll see that there is no bind for functions in IE until IE9. So Chrome, Firefox, and IE9 all support calling bind on functions but IE8 does not; this precisely matches the observed behavior.

Related

Rendering a closed Marionette view

Shouldn't a closed Marionette view re-delegate the defined events (events, modelEvents, CollectionEvents) when rendering again?
It seems as if I have to manually call delegateEvents after closing and re-rendering a view. Otherwise the view won't work as expected.
http://jsfiddle.net/4DCeY/
var app = new Marionette.Application();
app.addRegions({
main: '.main'
});
var MyView = Marionette.ItemView.extend({
template: _.template('Hi, I\'m a view! Foo is: <%= foo %>'),
modelEvents: {
'change': 'onChange'
},
onChange: function() {
alert('change!');
}
});
var Model = Backbone.Model.extend({});
app.addInitializer(function() {
var m = new Model({foo: 'bar'});
var myView = new MyView({
model: m
});
app.main.show(myView);
myView.close();
app.main.show(myView);
m.set({foo: 'baz'});
});
$(document).ready(function(){
app.start();
});
If I understand your question right, there are multiple open github issues about this.
For example:
https://github.com/marionettejs/backbone.marionette/pull/654
https://github.com/marionettejs/backbone.marionette/issues/622
Last time I checked, Derick (the creator of Marionette) didn't feel like reusing closed views should be something regions should do.
So you could
simply create a new view and show that one
manually call delegateEvents - but there was an issue with multiple event bindings that I can't remember right now, so be careful about that one (not at work right now, so can't take a peek at the code, sorry)
write your own region manager
or wait and see if Derick will merge one of the pull requests
a couple of points:
You do not need to call myView.close() Marionette Region will take care of that when you show another view
Marionette.Region will not replace the same view with itself. It will just skip the redundant procedure if you want to test this correctly you need 2 views
If you want a change in model to invoke render you must explicitly write it
I altered the jsfiddle with the following things:
added myView1 and myView2
removed explicit call to myView.close
added a call for this.render() from the onChange function
Here is the corrected jsfiddle http://jsfiddle.net/4DCeY/1/ :
app.addInitializer(function() {
var m = new Model({foo: 'bar'});
var myView1 = new MyView({
model: m
});
var myView2 = new MyView({
model: m
});
app.main.show(myView1);
app.main.show(myView2);
m.set({foo: 'baz'});
});
And:
onChange: function() {
alert('change!');
this.render();
}

Backbone js bind method on collection

I am following the tutorial at:
http://arturadib.com/hello-backbonejs/docs/3.html
This is a piece of code I am stuck on:
initialize: function(){
_.bindAll(this, 'render', 'addItem', 'appendItem'); // remember: every function that uses 'this' as the current object should be in here
this.collection = new List();
this.collection.bind('add', this.appendItem); // collection event binder
this.counter = 0;
this.render();
},
The line of code that I am having a hard time understanding is:
this.collection.bind('add', this.appendItem);
I know there is a bind method in underscore, but I don't think that is the same bind function.
Could you explain what the above line is for and where I can read more about it?
In backbonejs, a collection can trigger events. Example:
this.collection.trigger('myEvent');
In addition, you can also bind a collection to some events. Example:
this.collection.bind('myEvent', function() { ... });
Backbone.Collection.bind() method comes from Backbone.Events. Note that Backbone.Collection has all the methods of Backbone.Events mixed in (like all the other backbone.js objects including Backbone itself).
Backbone.Events.bind() is an alias for Backbone.Events.on().

Backbone Model on change save - infinite loop?

I'm clearly missing the obvious here, but it's been a long day already.
The following code creates an infinite loop in the browser:
M = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage("ModelName"),
initialize: function() {
this.on("change", this.save, this);
}
});
While the following code works fine:
M = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage("ModelName"),
initialize: function() {
this.on("change", this.modelChanged, this);
},
modelChanged: function() {
this.save();
}
});
What's the difference?
(Yes, I'm using local storage for a model rather than a collection, but the model is a singleton that doesn't exist in a collection.)
The change event passes arguments to its handler, and if save is called with arguments, it applies them as new attributes to the model, and causes a change event (which passes attributes to save... which causes a change... etc)

Is it possible to trigger an event when initializing a model?

In a backbone model, is it possible to trigger an event in the initialize function, for a nested view? I based my current code off this example: https://stackoverflow.com/a/8523075/2345124 and have updated it for backbone 1.0.0. Here is my initialize function, for a Model:
var Edit = Backbone.Model.extend({
initialize: function() {
this.trigger('marquee:add');
this.on('change', function(){
this.trigger('marquee:add');
});
}
...
}
I'm trying to call a method renderMarquee when the model is initialized:
var EditRow = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, "change", this.render); // works
this.listenTo(this.model, "marquee:add", this.renderMarquee); // only called when changed, but not when initially created
...
}
renderMarquee IS called when the model is changed, but not when it is initialized. 'change' events work as expected (this.render is called). Any thoughts?
Thanks!
I am currently facing a similar problem. I needed to trigger the change event in the initialize method of my model.
I looked into the backbone code which revealed why this is not happening:
var Model = Backbone.Model = function(attributes, options) {
...
this.set(attrs, options);
this.changed = {};
this.initialize.apply(this, arguments);
};
the set is executed before the initialize and this.change is emptied setting the model state to "nothing has changed".
In order to overwrite behavior this I added the following code to my initialize method.
initialize: function(attributes, options) {
...
this.changed = attributes;
this.trigger('change');
for (attr_name in attributes) {
this.trigger('change:' + attr_name);
}
},
I trigger all change events manually, this is important for me since inheriting models may bind to change or change:attrxy. But this is not enough, because if I just trigger the events the changedAttributes() method would return false therefore I also set this.changed to the current attributes.
This doesn't make a lot of sense because you are initializing the model somewhere prior to doing the view.listenTo call. Unfortunately, you don't really have a choice in that matter.
You are probably going to want to move the event handling to a Backbone.Collection which already has built in events you can listen on for adding/removing.

Backbone.js: retrieving a collection from the server in PHP

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.

Resources