The "change" event is not firing in the following code.
var PageView = Backbone.View.extend({
el: $("body"),
initialize: function(){
this.model.on("change:loading", this.loader, this);
},
loader: function(){
if(this.model.get("loading")){
this.$el.find('.loader').fadeIn(700);
}
else
this.$el.find('.loader').fadeOut(700);
},
});
var PageModel = Backbone.Model.extend({
defaults: {
loading: null,
},
initialize: function(){
this.set({loading:false});
},
});
$(function(){
var pageModel = new PageModel({});
var pageView = new PageView({model: pageModel});
})
It works if I'm adding this in the model's initialize
function:
setTimeout(function() {
this.set({'loading': 'false'});
}, 0);
I can leave it this way, but this is a bug.
Here's the order the code runs:
initialize
function is called, setting the loading
attribute to false
,"change:loading"
The event handler is never called because the event never occurs after it was registered.
First remove the set from the model.
var PageModel = Backbone.Model.extend({
defaults: {
loading: null
}
});
Then, after creating the view, set the loading
attribute.
var pageModel = new PageModel();
var pageView = new PageView({ model: pageModel });
pageModel.set('loading', false); // now the event should trigger
Since the listener is now registered before the model's loading
attribute is changed, the event handler will be called.
Use Backbone's best practices:
.listenTo
over .on
to avoid memory leaksel
property on the viewA view is an atomic component that should only care about itself and its sub-views.
While in your case, it wouldn't matter much that you use the el
property on the view, it still goes beyond the responsibilities of the view. Let the calling code deal with passing the element to use for this view.
var PageView = Backbone.View.extend({
initialize: function() {
this.model = new PageModel();
this.$loader = this.$('.loader');
this.listenTo(this.model, "change:loading", this.loader);
},
loader: function() {
this.$loader[this.model.get("loading")? 'fadeIn': 'fadeOut'](700);
},
render: function() {
this.loader();
return this;
}
});
Put the defaults where they belong.
var PageModel = Backbone.Model.extend({
defaults: {
loading: false
}
});
Here we choose the body
as the element to use for the view, using the el
option, and then call render
when ready.
$(function() {
var pageView = new PageView({ el: 'body' }).render();
});
The event won't be triggered by the listener right away, instead, we use the render
function to put the view in its default state. Then, any subsequent changes of the loading
attribute will trigger the callback.
I have listed the most useful answers I've written about Backbone on my profile page. You should take a look, it goes from the beginning to advanced and even provides some clever Backbone components that solves common problems (like detecting a click outside a view).