Backbone.js: Why can't I retrieve the model in this case? - backbone.js

I'm trying to retrieve the model, because I need to access certain attributes, but I cannot. I'm inside a view, which extends another one. This is how the view looks like:
var ImageGridControlView = GridControlView.extend({
//.... stuff ....
alert(this.model.get('property')) //This gives me an error in console and nothing is alerted.
});
In the class that the above code extends, the model can be retrieved, like this:
var GridControlView = ControlView.extend({
//.... stuff ....
alert(this.model.get('property')) //This shows the property value correctly
});
Why can't the model be retrieved from ImageGridControlView? What is the persistence of models across different classes, children, etc? I'm new to Backbone, and the official documentation only covers models superficially.

Your snippets are too small to actually find the problem, so we have to just guess. Most likely candidates are:
you are accessing this.model inside a function triggered by an event binding that is not properly bound, so this isn't actually your view instance. It could be a the source DOM element of the window object instead.
You aren't passing a model property in the options object provided to the view's constructor

Related

What does the "r" mean in chrome dev tools when you're looking at a Backbone Object?

I'm learning backbone and using chrome devl tools to look at objects in my browser. What does the 'r' mean?
It's the name of the constructor used to create the model object. However in this case the Backbone code has been minified.
If you create an instance of the base Model class it will be logged like this:
If you look at the Backbone source code you can find the constructor definition:
var Model = Backbone.Model = function(attributes, options) {
However, if your code is compressed the object will instead show up like this:
In your case you're using Backbone's extend mechanism to create a new model type that inherits from the base Model.
Uncompressed this will show up like this:
Again, we can look at the Backbone source code to understand why:
var extend = function(protoProps, staticProps) {
// ...
child.prototype.constructor = child;
// ...
return child
}
You can see that when the new model type is created Chrome doesn't have access to the name you want to use for the model. Instead it tries to do its best to infer the correct name from the variable names.
And then, after compression, you get the single-character class name that you are seeing in the console:

how model and view are connected in backbone.js

I am new in Backbone.js and I keep failing to understand how the model and the view are connected.
I played with Angular where things are pretty clear there, how model, view and controller are connected.
I know Angular and Backbone are different and the latter is MV*.
In Backbone I can understand how model and view are created and work, but how are they connected? Seems to me they are seperated.
Please take a minute to explain or point me to a tutorial.
Thanks in advance
EDIT
OK, here is an example. It happens that I read the book that trolle suggests. This is a code from the book's github
I start reading. I understand the Todo model. I understand the TodoList collection. Then I get to the TodoView
creates a new li
uses Underscore template to compile html
defines some functions that imlements later in the same view
defines an initialize function
inside that function
what is this? this.model.bind('change', this.render, this);
how he can magically bind the action change to a model? How the code knows about the model? When he defined the model and how? Just because is there, the code knows that model = Todo model?
How does he do that bind? What am I missing.
This confuses me, so reading AppView view does not help me much
Thanks again
In backbone.js views are used for displaying models in browser.
For example you can have a model object, whose JSON representation resembles the following: {'firstName': 'foo', 'lastName': 'bar' }
And you use view object to map this model to browser DOM.
As a rule, you use view object along with certain template engine.
Templates allows for creating html chunks filled with model's data.
If you are using underscore template function, your template may look something like this:
<div>
<div>First Name: <%= firstName %></div>
<div>Last Name: <%= lastName%></div>
</div>
After merging template with model's data it would be:
<div>
<div>First Name: foo</div>
<div>Last Name: bar</div>
</div>
You can reuse this view object and its template to display another model object, for example {'firstName':'another foo', 'lastName':'another bar'}, so that the result html would be:
<div>
<div>First Name: another foo</div>
<div>Last Name: another bar</div>
</div>
That is one thing about connection between model and view.
Also view object can listen to changes in your model object to render immediately last updates. For example (inside view object):
initialize: function() {this.listenTo(this.model, 'change', this.render);}
In short, views are the logic behind the presentation of the model's data to the user. So in its simplest form, you bind a model to a view through the models change events, so you can update the presentation instantly whenever your data changes. So a simple view would take in a model, create HTML elements based on that models data, insert that html into the DOM and update that HTML whenever the data changes.
You can find a great book full of helpful examples here (free): http://addyosmani.github.io/backbone-fundamentals/
EDIT:
With regards to your updated question about how the view knows about the model, this.model is a reference to the actual model object. You can set the reference to the model when you create the view. That is, when you call your view-constructor to instantiate a view, you could pass in a model. You need to go all the way into the AppView object in the code example to see where this happens, in the addOne method:
addOne: function(todo) {
var view = new TodoView({model: todo});
this.$("#todo-list").append(view.render().el);
}
The function gets a model as a parameter, and when the view is instantiated that model is referenced. So now you have a view that knows about the model, and when the view.render method is called, the view can render it's template with the model data. When you change the data in the model, for instance by using the set method, myModel.set({title: "March 20", content: "In his eyes she eclipses..."});, you trigger the change event for that model. You can see all the built in events for backbone here: http://backbonejs.org/#Events-catalog. The view is listening for that event, just like it could listen for a click event or any other event. In the code in your example the view listenes for a change event from the model. If it hears it it knows that the object behind this.model has changed, and it can then update the DOM with the new data or do something else. In the case of the example it calls this.render, which updates the DOM with the new model data.
I think you want to know about Backbone.Events (http://backbonejs.org/#Events), both Models and Views make use of this module and that's how the view learns about changes in the Model, if you want to learn how this is implemented you can always read the annotated source (http://backbonejs.org/docs/backbone.html#section-19), but more important I think you want to learn about the observer pattern: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjavascript.

Binding to property of model attribute in Backbone.js

Disclaimer: I'm new to Backbone.js (coming from AngularJS), so I may have an inaccurate mental model of how this is supposed to work.
I have an object, characterNodes, which I'm making an attribute on my model. characterNodes looks something like this:
var characterNodes = {
character_1: {
stories: [// list of Stories]
},
character_2: {
stories: [// list of Stories]
}
...
}
My Backbone Model looks something like this:
var StoryGraph = joint.dia.Graph.extend({
initialize: function() {
// Call parent constructor
StoryGraph.__super__.initialize.apply(this, []);
this.set('characterNodes', characterNodes);
this.on('change:characterNodes', function() {
alert('test');
});
}
});
Each Story has a property "isUnlocked" which is changed elsewhere in the application. I want to fire an event (ie. that is, the alert 'test' should pop up) whenever this property is changed. With the code as it is above, the event never seems to fire.
I can't get a clear understanding from the Backbone docs whether this is supposed to work - does on('change:characterNodes') fire whenever any property (or sub-property, or sub-sub-property, etc) of characterNodes changes? Or only when the pointer to the object changes, that is, when it's replaced with another object? Or am I doing something else wrong? Thanks!
Backbone doesn't do any magic, basically, the change event is fired only if you set the "characterNodes" to a new object. If you're changing a nested property of that object, Backbone doesn't know it happened. You have three options: a) Change the whole object (e.g. by creating a copy), b) fire the change event manually (m.trigger("change:characterNodes")) whenever you change a nested property, c) Do not use nested objects for this. Have "character1_Stories" as a top level property.
Options c) is preferable. Try to keep properties in your models flat. Option a) is also fine but it has the disadvantage of having to copy the object. Option b) is not recommended. This is because Backbone keeps track of the previous value of the model properties (m.previous("characterNodes")). If you change a nested property, the previous value will have the same reference to the same object as the new value, therefore, it won't reflect its previous state.
Try to call a function instead define the function, and pass the third argument to keep the context call.
Something like this:
this.on('change:characterNodes', this.AlertSomething, this);
Hope it helps.

how to sync a backbone view with the attached model on bootstrap/creation?

Usually I have a View on which I set listeners to model-changes, like so (conflated):
var jsonModel = {bla: 'interesting stuff'}; //some model in json, probably rendered in a dom-element and passed from the server to the client
var someModelType = Backbone.RelationalModel.extend({
bla: "String"
});
var someModelInstance = new someModelType(jsonModel);
var someViewType = Backbone.View.extend({
initialize: function(){
this.listenTo(this.model,'change:bla', function(model){
//update view to sync with model change here
}
}
});
var someViewInstance = new someViewType({
model: someModelInstance
});
I'm looking for the preferred / backbone - way of bootstrapping the view, i.e: I want my view (form fields or what have you) to be synced with the model on creation. With the above this is impossible since the model is created before the view is attached, which results in model-updates/changes to be fired before listeners from the view were initialized.
Of course I could code some custom bootstapping logic which would call the listener-functions manually, but since this must be such a common problem, I'm looking on some best practice advice, or even better, a Backbone-switch I need to set to get this to work.
This will sound silly, but what I usually do is do a render() after the view creation.
For me, the whole point of the change event is to notify of a change. The fact that it's triggered also on creation (as a "changed compared to the default") is more a side effect than anything deep...
In that sense, when I create the views, and give it an existing model, I expect the model to be somewhat ready and so I should be able to do a render() right away. So my bootstrapping code would be more like:
var someViewInstance = new someViewType({
model: someModelInstance
});
someViewInstance.render()
Or do it in the initialize() after bindings if you feel adventurous (I personally don't like that because when dealing with subviews, it's hard to know exactly when you want the render to happen, especially when dependent on libraries who need the DOM to be ready, and can't work on detached nodes).
So to sum up, although your problem is common, to me, it doesn't need any kind of complex solution: you have a render() function to transform your model into DOM-stuff, your model may or may not be ready but it exists, so you pass it to the view, and you do the first rendering after creating the view, manually, because you know it needs to happen. Then you bind to changes, in case your model is updated, to update the DOM accordingly.
side note: in case of a collection of models, when the Model isn't ready at ALL, then it probably shouldn't even have an instance, and you should let the Collection give you some add/remove events for when it pops into existence.

Backbone.js: Natively passing attributes to models when fetched with a collection

Let say you are defining a Backbone.js Model. From the documentation we have ...
new Model([attributes], [options])
This seems great for passing some default attributes to a model. When passed the model automatically inherits those attributes and their respective values. Nothing to do. Awesome!
On they other hand lets say we have a Collection of that model.
new Backbone.Collection([models], [options])
Okay, cool we can pass some initial models and some options. But, I have no initial models and no options I need to pass so let's continue. I am going to fetch the models from the server.
collection.fetch([options])
Well I don't have any options, but I want to pass some attributes to add to each models as it is fetched. I could do this by passing them as options and then adding them to the attributes hash in the initialize for the model, but this seems messy.
Is their a Backbone.js native way to do this?
You can pass the attributes as options to fetch and over-ride the collection's parse method to extend the passed options (attributes) on the response.
The solution would look like the following:
var Collection = Backbone.Collection.extend({
url:'someUrl',
parse:function(resp,options) {
if (options.attributesToAdd) {
for (var i=0;i<resp.length;i++)
_.extend(resp[i],options.attributesToAdd);
}
return resp;
}
});
Then to add attributes when you call fetch on the collection, you can:
var collection = new Collection();
collection.fetch({
attributesToAdd:{foo:'bar',more:'foobar'}
});
You may have to tweak the code a bit to work with your JSON structure, but hopefully this will get you started in the correct direction.

Resources