What's the purpose of using _.bindAll(this, 'render'); in the initialize function of a Backbone view?
It's binding your functions to the right context, the context of your view. So you are sure you're not calling it by mistake in a different context.
See this : When do I need to use _.bindAll() in Backbone.js?
Related
My Backbone.View looks this way:
define(["promise!table_config", "BBModel"], function (config, myModel) {
"use strict";
return Backbone.View.extend({
initialize: function () {
this.model = new myModel({
foo: config
});
...
},
render: function () {
...
}
});
});
Is is a good or bad practice to initialise a model inside a view?
Particularly in this case, in a require.js module where 'config' is a require-promise, motivating me to put put a model inside a view.
While Backbone does have a Model and a View class, it is not a strict MVC framework (eg. it lacks a Controller class). The Backbone documentation page explains as much (emphasis mine):
How does Backbone relate to "traditional" MVC?
Different implementations of the Model-View-Controller pattern tend to disagree
about the definition of a controller. If it helps any, in Backbone,
the View class can also be thought of as a kind of controller,
dispatching events that originate from the UI, with the HTML template
serving as the true view. We call it a View because it represents a
logical chunk of UI, responsible for the contents of a single DOM
element.
Given that, even though (as #Evgenly mentioned in the comments) "its not view responsibility to instance model, its controllers task" ... since the Backbone View is (conceptually) a controller, it absolutely makes sense to create your models inside your views.
But putting that theory aside, here's a more practical answer. I work on a 3+ year-old Backbone app, along with two other developers (and more previously). In that app the vast majority of all models get created inside views (the few remaining ones get created inside routes). Not only has this not been a problem for us, but I can't even imagine any other way of doing it.
I'd like to call a method in my view when its attributes property is updated. The following code gives me an error: undefined is not a function.
SimpleView = Backbone.View.extend({
initialize: function(){
this.attributes = _.extend(this.attributes, Backbone.Events); // update
this.attributes.on('change', this.updateAttributes(), this);
}
});
How can I elegantly bind an event listener to the attributes?
JSFiddle here
UPDATE: I figured I have to extend attributes with Backbone.Events so I can listen to changes. Yeah... So now I don't get anymore errors, but still nothing happens. Any help would be largely appreciated.
Although I would recommend you to use a model for holding the attributes, if you want to stay with your current approach, you need to manually trigger the event.
this.attributes.trigger('change');
I have updated your JSFiddle so you can get an idea of how it works.
Angular uses dirty checking to catch the data change and accordingly change the model or view. Ember sets observers to each elements to do this process.
But how does backbone do it?
Backbone doesn't handle data changes for updating views (by default).
Let's see.
Backbone.Model has all the methods of Backbone.Events. So when the data changes, Model calls this.trigger() function.
But you should subscribe on "change" event to handle all data manipulations:
myModel.on('change:attributeName', myView.render, myView);
Maybe it would be better to say that Backbone uses setters and Backbone.Events to observe data changes.
As #Leestex answered, backbone doesn't bind data changes for you.
but, it's better to use listenTo inside of the view. eg:
MyView = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, 'change', this.render);
}
});
see:
http://backbonejs.org/#Events-listenTo
Another way for the view to listen to model events is to attach a handler to the model, like this:
this.model.on('change', this.render);
But it's generally not recommended because you would have to detach the handlers manually using off(). When you're listening to multiple models, this can get messy and may lead to
zombie views.
The benefit of using listenTo() is that you can call stopListening(), which will unbind the callbacks associated with that object. In this case, that object is the view.
I'm having a hard time understanding the what "this" is referring to in the Todo.js tutorial in Backbone.js. In specific, inside the AppView:
initialize: function() {
this.input = this.$("#new-todo");
this.allCheckbox = this.$("#toggle-all")[0];
Todos.bind('add', this.addOne, this);
Todos.bind('reset', this.addAll, this);
Todos.bind('all', this.render, this);
this.footer = this.$('footer');
this.main = $('#main');
},
So when Todos.bind('add', this.addOne, this) is called, it is binding the view (this.addOne) to the collection ('add'). If so, we assume that the third parameter ("this") is also referencing to the AppView object. Why do we need to have "this" as a third parameter?
Annotated source code: http://backbonejs.org/docs/todos.html
The this as the third argument is the this context to set when the function in the second argument is invoked.
That sounds very confusing, so let me try and break it down. Let's look at this line...
Todos.bind('add', this.addOne, this);
Had we used this instead...
Todos.bind('add', function() { this.$el.text("Hello, world!"); });
...we wouldn't need the third argument at all. In this case, the function is called immediately and its this is preserved.
However, because we don't want to inline every function, we pass a reference to the function like so...
Todos.bind('add', this.addOne, this);
This is the same as in your example. When you call a function like this.addOne(), its this is set to the this it was called on.
However, when you pass a reference (don't immediately invoke it), its this context is lost, and when invoked, it will be window, which isn't what you want.
To prevent this, the third argument is the this context to use when the function is invoked.
Internally, Backbone will use _.bind() to bind it. This is a polyfill for the native bind() method on functions.
I've been looking at some examples of backbone.js based application. I notice that in some (such as this example below) the underscore function _.bindAll() is used:
initialize: function (args) {
_.bindAll(this, 'changeTitle');
this.model.bind('change:title', this.changeTitle);
},
whereas in others (such as the todo app below) do not:
initialize: function() {
this.model.bind('change', this.render, this);
this.model.bind('destroy', this.remove, this);
},
What is the purpose of _.bindAll() in this context, and is it necessary?
_.bindAll() changes this in the named functions to always point to that object, so that you can use this.model.bind(). Notice that in your second example, a third argument is passed to bind(); that's why using _.bindAll() isn't necessary in that case. In general, it's a good idea to use for any methods on the model that will be used as callbacks to events so that you can refer to this more easily.
In Detail: _.bind(ctx, 'method')
Takes your method, creates a copy with the context bound to 'ctx' and adds the copy as property.
This is a workaround for jQuery.bind() not allowing you to pass in a context.
JQ will always call the callbacks with a undefined context. Backbone is built on jQuery.
See here: http://backbonejs.org/#FAQ-this