I use backbone.js and have a model without a collection.
In the view I call fetch on the model with a callback to render the view.
this.user.fetch({success: function(d) { self.randomUserView.render() }})
how can I make the view update automatically when the model change? e.g. I don't want to specify the above callback every time I call fetch. I tried to bind the view to many model events on initialize but this did not work.
On the view, add an event handler to the view's model:
initialize: function() {
this.model.on('change',this.render,this);
}
Backbone is event-driven, not callback driven framework (although technically they are callbacks). And your approach does not seem to be native to Backbone. When you do fetch(), user model will automatically trigger "add" event. All you need to do is in the corresponding view add this in initialize:
initialize: function() {
... your code...
this.model.bind('add', this.render);
}
This way you subscribe to this even only once in the view init and don't have to ever pass explicit callbacks.
Actually if you want to have a view refresh on fetch on a collection you need to bind RESET!
this.model.bind('reset', this.render, this);
Update is only fired if the current collection is edited.
ps bindAll is dangerous and lazy. (and probably going to cause you problems down the line)
Related
I've been studying this Backbone tutorial and came across this bit of code:
var LibraryView = Backbone.View.extend({
el:$("#books"),
initialize:function(){
this.collection = new Library(books);
this.render();
},
...
});
The author explains that this.render() makes the view render when its constructor is called.
When would you not want the view to self-render?
The render method does nothing more than adding HTML to the DOM by using jquery's html or append methods. Where you choose to call the render method on the view is an architectural choice.
Backbone is said to apply the MV* pattern where models are bound directly to the view.
As such, the view would render itself upon instantiation, and have the ability to render itself when the model changes.
However, Backbone leave a lot of decisions to the developer and is a flexible library. There is nothing that technically prevents you to use control objects that manage the flow of the views; as such, another object can instantiate and re-render the view as well.
var view = new Bb_View();
view.render();
In the end, render is just a method on the view, and you can define your own methods as well. An example reason why I would call custom methods on the view from outside the view, is when I keep reference to an array of views.
As an event occurs, I loop through the views, I might add some conditions, and then call the custom method on particular views based on the condition.
I have a CompositeView with a collection. The CompositeView has its own model and view that is pretty complex and changes made it to its childViews need to trigger the CompositeView's view to re render but not its childViews. Is that possible?
I read in the Marionette docs "You can specify a modelView to use for the model. If you don't
specify one, it will default to the Marionette.ItemView." But nothing happens when I try to use this in my CompositeView.
Your CompositeView should listen for change event of own model and should call render on that. In this way the only ItemView associated with the composite view will re-rendered on change of own model not all childView.
this.listenTo(this.model, 'change', this.render)
You can put this line inside initialize method of CompositeView.
I have seen examples of managing backbone views and events attached to them in a single page architecture, where you can use view.remove(); for the current view and call the next view using require.js syntax, whenever you navigate to a different view. But can this be also achieved by simply emptying the html of the current view using $(div).html('');? Are both approaches going to have the same result? What are the differences, if any?
One simple difference is
.remove()
Removes a view from the DOM, and calls stopListening to remove any
bound events that the view has listenTo'd.
If your view has these 2 listeners in the initialize.
initialize: function() {
this.listenTo(this.collection, 'reset', this.render);
this.listenTo(this.model, 'change', this.render);
}
Your vies is listening to the model and the collection. So if you use remove to get rid of the view it will unbind the events for the view.
Where as if you do $('div').html() there is still a reference to the view and the methods on the view are still present in the memory along with the listeners which will cause memory leaks in your application. This is just one of the difference and there might be many more.
Simple question: What is the best way to fire an event 'only once'?
I have a Collection in backbone with multiple models. Sometimes multiple models' attributes are updated at once, firing multiple 'change' events on the Collection.
What is the best way to fire the 'change' event only once, grouping all attribute changes together?
My best idea at the moment is using a timer but this will only capture the first model attribute change.
All suggestions, ideas and solutions are valued. Thank you :).
Context:
In this case the event fires a very computationally intensive function, therefore multiple runs must be avoided.
Whenever you are updating the attributes on the models in bulk, pass {silent:true} as an option, which will suppress any events from being fired.
Then trigger a custom event for which a view or multiple views listening to the same.
You can "debounce" the method in your collection that responds to change events, though you'll have to manually figure out which models changed:
Col = Backbone.Collection.extend({
modelChanged: _.debounce(function() {
// handle model changes
}, delayInMs),
initialize: function() {
this.collection.on('change', this.modelChanged, this);
}
})
You can try resetting the collection, rather than adding the elements. See Collection reset method description:
reset collection.reset([models], [options])
Adding and removing models one at a time is all well and good, but sometimes you have so many models to change that you'd rather just update the collection in bulk. Use reset to replace a collection with a new list of models (or attribute hashes), triggering a single "reset" event at the end. Returns the newly-set models. For convenience, within a "reset" event, the list of any previous models is available as options.previousModels.
As documentation says, if you use reset, instead of add you will get a single reset event in the end.
I don't understand why should I bind on render inside view Initialize?
example:
_bindAll(this, "render") ??
You do not have to, but if your render is triggered based on an event, you will want it to run within the context of your view (this). If you have something like this:
this.model.bind("change", this.render)
The change event will be run within the context of the model, but you want the render call to run against the view.
To simplify all this binding, you can use _.bindAll and list all the methods you might call from outside your views. You should know that Backbone is auto binding the callbacks used for UI events:
events: {"click" : "render"}