Reputation: 359
I just realized that I have no idea what the heck I am doing when it comes to backbone. I came to this realization when trying to figure out a cogent strategy for removing the view's event listeners on the model. Then I asked "well, where is the model anyways now that the view has been rendered to the DOM?" and then I asked "how is this model object that I created inside a function body, and is therefore out of scope now that I have rendered the view to the DOM, maintaining state?" AHHHHHHHH!!!!!!!!!
Ex.
View Constructor
Timeclock.Views.JobNewView = Backbone.View.extend({
template: JST['jobs/_form'],
events:{
'blur #job_form :input':'assignValue'
},
initialize: function(options){
this.listenTo(this.model, 'failed-request', this.failedLocationRequest);
this.listenTo(this.model, 'updated-location', this.updatedLocation);
this.listenTo(this.model, 'sync', this.renderJobView);
this.listenTo(this.model, 'invalid', this.displayModelErrors);
this.listenTo($(window), 'hashchange', this.clearListeners);
},
render: function(){
this.$el.html(this.template({attributes: this.model.attributes}));
this.$el.find('#address_fields').listenForAutoFill();
return this;
},
assignValue: function(e){
var $field = $(e.currentTarget)
var attr_name = $field.attr('name');
var value = $field.val();
this.model.set(attr_name, value);
}...
});
Function rendering view to the DOM
renderCollaboratingView: function(e){
var job = this.model;
var $row = $(e.currentTarget);
job.set({customer_id: $row.data('id')});
var model_view = new this.ViewConstructor({model: job});
$container.html(model_view.render().el);
}
So how is the model that I am passing to the view object persisted so that the DOM interactions can set attribute values on the underlying model object?
I understand that backbone views are just a wrapper to declaratively write DOM listeners but how are DOM events acting on the underlying model object in the example above? As soon as the renderCollaboratingView() function has exited how is the model that I passed to the view still being interacted with?
I can think of two ways:
1) The model object is bound to the DOM through a jquery object. All the event listeners that I declare in my view all know where the underlying model object lives on the jquery object(the 'model' attribute?).
2) Backbone is creating some object namespace that the view knows about where it stores models and collections that back the DOM. I have a feeling it's #1 but who knows.
Once again, I got here because I was trying to understand why I need to remove the listeners on the model that I passed into view in the first place. If backbone views are really just jquery objects then aren't jquery listeners removed from DOM elements when the element backing the jquery object is removed from the DOM? Do I only need to remove the listeners if I am going to not destroy the view entirely and save it for later use?
Any help that can be given would be greatly apprecaited. Having an existential crisis.
Thanks
Upvotes: 3
Views: 909
Reputation: 5301
JavaScript has garbage collection. Objects do not get destroyed then they go out of scope. An Object X
get garbage collected by the runtime system, when it sees that nobody is having a reference (or is pointing) to X
.
A Backbone View
is also an object. An object can store reference to another object.
In your renderCollaboratingView
, you wrote :
var model_view = new this.ViewConstructor({model: job});
this model_view
is your view's object. You passed your job
which is your model you got from :
renderCollaboratingView: function(e){
var job = this.model;
....
}
You can look at this line in backbone annotated code : BackBone View Options. (I would suggest to look at the link after you have read the answer)
The line is :
var viewOptions = ['model', 'collection', 'el', 'id',
'attributes', 'className', 'tagName', 'events'];
and then Backbone View is defined as : BackBone View
It is :
var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
options || (options = {});
_.extend(this, _.pick(options, viewOptions));
this._ensureElement();
this.initialize.apply(this, arguments);
};
Look at line :
_.extend(this, _.pick(options, viewOptions));
and your code :
var model_view = new this.ViewConstructor({model: job});
So how is the model that I am passing to the view object persisted so that the DOM interactions can set attribute values on the underlying model object?
If you merge the dots : You are passing a model
to your view. You can also pass other like 'collection', 'el', 'id', ...
in viewOptions
.
They get pick
from your passed object {model: job}
and extended
in the view object.
This is how your view object has reference to the model that it was given.
Once again, I got here because I was trying to understand why I need to remove the listeners on the model that I passed into view in the first place.
As i said, just removing a view-object
from DOM is not going to destroy
it. You would have to remove all references of view-object
that other objects (here model
) have.
When you said :
initialize: function(options){
this.listenTo(this.model, 'failed-request', this.failedLocationRequest);
....
in your view. You told the model
to call your view-object's failedLocationRequest
on model's event failed-request
. This is possible only when your model's object
would store reference to view's object
. So, your view is not destroyed.
view-object(s) not in dom would continue receiving such events from models and all other places where they registered (except dom) and would do things in the background, that you just never wanted. Definitely not what you wanted..
simple advice, call remove
on your view. BackBone View remove
and read stopListening
Upvotes: 1
Reputation: 2092
So how is the model that I am passing to the view object persisted so that the DOM interactions can set attribute values on the underlying model object?
Backbone Models and Views are simply Javascript objects that live in-memory in the scope of the page (like any other Javascript). If you were to do ...
var name = 'Peter';
var person = new Backbone.Model({ name: 'Peter' });
var view = new Backbone.View({ model: person } );
... then name
, person
, and view
are all just objects in memory. They have no relation to jQuery; they have no relation to the DOM. The View happens to be able to create DOM elements if you implement render()
, but even then those elements don't ever have to ever be attached to the page's live DOM at all.
... how are DOM events acting on the underlying model object in the example above? As soon as the renderCollaboratingView() function has exited how is the model that I passed to the view still being interacted with?
Based on the code you've shown, the model isn't being interacted with directly. Your events
hash ...
events:{
'blur #job_form :input':'assignValue'
},
... does say that any time a blur
event happens in the job_form
element, it will call a method on the view called assignValue
. That method may interact with the model (it probably does, right?), but DOM events don't directly cause interaction with the model at all.
If backbone views are really just jquery objects then aren't jquery listeners removed from DOM elements when the element backing the jquery object is removed from the DOM?
Backbone's listeners are wholly different than jQuery listeners. They listen for Backbone-centric events. See here for the list of built-in events that Backbone components fire. A View's events hash is a nice convention that is used to listen for DOM events; it's basically a nice wrapper around the jQuery concept of event delegation.
Do I only need to remove the listeners if I am going to not destroy the view entirely and save it for later use?
If you don't remove listeners, they will continue to run whenever the related event happens, regardless of whether the listening component is changing the page. Suppose you had a Backbone.View that did something like this:
var MyView = Backbone.View.extend({
// ...
events: {
// Don't do this!
'click': '_onClick'
},
// ...
_onClick: function() {
this.$el.append('Clicked!');
}
});
Any time any click
DOM event happens on the page, this view will append the text Clicked!
to its internal DOM element. When the view is attached to the page's DOM, Clicked!
would appear on every click. When the view was removed from the DOM, the function would still run on every click... but since the View's internal root element wasn't attached to anything the function would have no effect.
It's a type of a memory leak, as any instance of MyView
will ever be cleared up by the garbage collector. But the particularly nefarious side effect is it also uses CPU time to do something that is completely worthless. Now imagine if the event listener did anything of consequence. Performance of the page will suffer.
Upvotes: 2