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().
Related
I'm using NestedModel change plugin to support nested model changes into my models.
I tried this into my View where I got a reference to a collection.
....
initialize: function(){
this.listenTo(this.collection, 'change', foo);
},
foo: function(model){
console.log("Some model changed")
}
By doing FooModel.set({"fooPropertie.BarPropertie": "fooBarValue"});
I would expect to trigger a collection change event.
Am i doing something wrong?
It is hard to define the trouble since you do not provide the way this.collection was defined. But I see at least one thing to tune: this.listenTo(this.collection, 'change', this.foo);. Add this. before foo call. Currently in your context foo is undefined.
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.
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.
This is a 5 part backbone.js hello world tutorial/application. https://web.archive.org/web/20180317062059/http://arturadib.com/hello-backbonejs/docs/3.html In part 3, the author's illustrating how to use collections and models to store data and how to tie changes to the views.
I understand most of it, except this line
this.collection.bind('add', this.appendItem); // collection event binder
Is this 'bind' just binding context, or is it functioning as an 'event' such appendItem is called anytime that a model has been added?
I ask, because in the render method of the ListView, it's explicitly calling appendItem method, so why is it bound to 'add'
_(this.collection.models).each(function(item){ // in case collection is not empty
self.appendItem(item);
}, this);
Can someone please explain a little how that code is working. i looked through the documentation but couldn't find an explanation of bind used in this way.
Full code
(function($){
¶
Item class: The atomic part of our Model. A model is basically a Javascript object, i.e. key-value pairs, with some helper functions to handle event triggering, persistence, etc.
var Item = Backbone.Model.extend({
defaults: {
part1: 'hello',
part2: 'world'
}
});
¶
List class: A collection of Items. Basically an array of Model objects with some helper functions.
var List = Backbone.Collection.extend({
model: Item
});
var ListView = Backbone.View.extend({
el: $('body'),
events: {
'click button#add': 'addItem'
},
¶
initialize() now instantiates a Collection, and binds its add event to own method appendItem. (Recall that Backbone doesn't offer a separate Controller for bindings...).
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();
},
render: function(){
¶
Save reference to this so it can be accessed from within the scope of the callback below
var self = this;
$(this.el).append("<button id='add'>Add list item</button>");
$(this.el).append("<ul></ul>");
_(this.collection.models).each(function(item){ // in case collection is not empty
self.appendItem(item);
}, this);
},
¶
addItem() now deals solely with models/collections. View updates are delegated to the add event listener appendItem() below.
addItem: function(){
this.counter++;
var item = new Item();
item.set({
part2: item.get('part2') + this.counter // modify item defaults
});
this.collection.add(item); // add item to collection; view is updated via event 'add'
},
¶
appendItem() is triggered by the collection event add, and handles the visual update.
appendItem: function(item){
$('ul', this.el).append("<li>"+item.get('part1')+" "+item.get('part2')+"</li>");
}
});
var listView = new ListView();
})(jQuery);
Say your collection already has 10 models. Then you pass it to your view. You'd call render() which triggers a loop of appendItem() or what not. Your view is happy.
Then you add a model to your collection. (Model 11)
Rather than re-render the whole thing, the this.collection.on('add', this.appendItem, this) executes the function that adds a single item view to the already existing view list.
That's probably why it's bound to the add event AND included in the render as a loop. One loops through an existing collection to generate views at the start. One takes care of any new models that are added after the view is initialized and rendered the first time.
In backbone's todo demo the code has a few spots where _.bindAll(this,...) is used. Specifically it's used in the initialize function of both views. As far as I can tell it's necessary to do the following:
this.$('.todo-content').text(content);
But why would one want to do the above, when one can do:
$('.todo-content').text(content);
?
_.bindAll( this, ... ) is necessary not only for this.$( selector ).doSomething() but generally to be sure that this in your view's method is always pointing to the view itself.
For example, if we want to refresh our view when the model changes, we bind the view's render method to the model's change event:
initialize: function() {
this.model.bind( 'change', this.render );
},
Without _.bindAll( this, 'render' ), when the model changes this in render will be pointing to the model, not to the view, so we won't have neither this.el nor this.$ or any other view's properties available.
As of Backbone 0.5.2, it's no longer necessary to use _.bindAll(this...) in your views to set the context of the "bind" callback functions, as you can now pass a 3rd argument to bind() that will set the context (i.e. "this") of the callback.
For example:
var MyView = Backbone.View.extend({
initialize: function(){
this.model.bind('change', this.render, this);
},
render: function(){
// "this" is correctly set to the instance of MyView
}
});
this.$ limits jQuery's context to the view's element, so operations are quicker.
Additionaly, this.$('.todo-item') won't find your elements with todo-item class outside your view's element.