backbone.js model and collection overhead - backbone.js

When I fetch models or collections from the server, I am not able to access properties of the model unless I stringify then re-parse. Presumably the models themselves have some extra overhead from backbone.js? Note that in the below code I can perform stringify/parse sequentially, which is supposed to give the same result as I started with. However, clearly I have killed off some superfluous info by performing these two steps because my model's properties are now exposed differently from before. Surely I do not need to go through these two steps to access my model properties, right?
Eg.
thismodel = /// assume this came from server fetch
alert(thismodel.name); // DOES NOT WORK - undefined
jsonmodel = JSON.stringify(thismodel);
var providerprefslistJSON = jQuery.parseJSON(jsonmodel);
alert(providerprefslistJSON.name); // WORKS

Backbone Model objects are not plain old JavaScript objects. They keep their attributes in an internal hash. To access the name attribute you can either do this:
alert(thismodel.attributes.name);
Or better yet use the get() method:
alert(thismodel.get("name"));
The reason it works when you convert the model to JSON and then back again is because JSON.stringify calls the toJSON() method, which creates a JSON string from the internal attributes hash, meaning when you parse that string you get a plain old JavaScript object - which is not the same as a Backbone Model object.

First, are you trying to access the property of the model or response?
From alert(thismodel.name) it would seem that you're going for a property of the model not the attribute. If you're looking for the model attribute then perhaps you want alert(this.model.get('name'))
If you're indeed going for model.name, then basically the problem may lie in how you're parsing the data. Say for example the JSON from your server is like this {'name':'Jimmy'}.
While the model.response the raw JSON sent has "Jimmy" namespaced under object.name, Backbone will automatically take that and turn it into a model attribute unless instructed otherwise (e.g. modelObj.attributes.name) at which point you'd use the get() function.
You should be able to access model data fairly simply if everything works.
E.g. Fetch
var model = new MyModel();
model.id = 1;
model.fetch({
success: function(model, response) {
console.log(model.get('name')); // The model name attribute
console.log(response.name); // The RAW response name property
}
});
Or maybe your server isn't sending the data back as JSON data. Is the server response content-type="application/json" ?
Some things to check.

Related

ng-repeat convert resource objects to simple objects

Iam getting my data with help of the Angular's $resource service as array. Each element of that array is an Resource-Object. So i can use methods like $save and $update of these Objects. In a view i represent my array with the help of the ng-repeat directive like:
<div ng-repeat="appointment in object.appointments" ng-click="editAppointment(appointment)">
And here i get in trouble. The appointment-Object i get in the editAppointment-Method is a simple Object. No Resource Object anymore. So i cant use the helpfull methods like i mentioned above.
$scope.editAppointment= function(appointment){
console.log(appointment); // > Object
console.log(object.appointments); // > Array of Resource
}
Have somebody noticed that problem too? May its a bug, but i cant imagine that.
Assuming your resource class is called Appointment, you should just be able to do:
$scope.editAppointment= function(appointment){
new Appointment(appointment).save();
}
Presumably your Appointment resource looks something like the following (i.e. it correctly maps some sort of id property from existing objects to the URL parameters):
var Appointment = $resource('/appointment/:appointmentId', {appointmentId:'#id'});
This would be the case if your appointment objects (i.e. the underlying JSON objects handled by your API) have an ID property called id. If it's called something else (or if there are multiple path variables in your URL) you'll just need to change the second argument to map all of the properties of the objects being saved (the values starting with '#') to the URL path variables (the things starting with ':' in your URL).
See where they save a new card in the credit card example here: http://docs.angularjs.org/api/ngResource.$resource. The fact that they're dealing with a totally new object and that you're trying to save an existing one is irrelevant. Angular doesn't know the difference.

Access data from a local model

Here is my problem, I hope some one can help me here, I'm developing a mobile app in qx.
In the application.js I call for a JSON in a server through the qx.store.json(url) that creates a model that I bind to a offline model to access the data offline in the app.
Everything good so far then when I try to access the data in the offline model it doesn't let me. The original JSON data is
array(timestamp=>time(),
userdata=>array(
array(userid=>0...),
array(userid=>1...)))
When I debug the JSON or the offline data with obj.getItem(1) it always returns me qx.data.model.userdata.
I'm trying to use the data inside the array of userdata to validate a user in a foreach statement but qx.data.model.userdata always returns undefined.
I try obj.getUserdata(), obj.getItem(1), obj being the offline model.
What am I doing wrong? It isn't a model a store for data, or it can only be used as binding data to an widget?
If the item at index 1 was an Array, obj.getItem(1) would return an instance of qx.data.Array. Since it returns an instance of qx.data.model.userdata, that means the model item is actually an object with a single property named "userdata" and you would access the value by calling obj.getItem(1).getUserdata().

backbone collection fetch() returns one record only

I am trying to fetch list of objects from server, the collection accepts the first record and ignores the rest.
the response from the server is of type application/json
[{"id":1,"name":"A"},
{"id":2,"name":"B"},
{"id":3,"name":"C"}]
in client side,
var collection = new Backbone.Collection;
collection.url = 'url_to_the_resource';
collection.fetch();
console.log( collection.toJSON());
the output is
[{"id":1,"name":"A"}]
edit
I call log when the collection is ready, i.e. after the asynchronous call is complete as follows:
collection.fetch().done( function() {
console.log( collection.toJSON() );
});
and still getting one record. I have also checked backbone.js source code, and found the following at line 682
// If a duplicate is found, prevent it from being added and
// optionally merge it into the existing model.
if (existing = this.get(model)) {
and added a logging right after that line, and found that backbone merges all models, why?
I can only guess. If you can provide collection code it would be great.
Be sure model idAttribute (if it is overridden) is unique.
Check your collection parse method if it is overridden. It should return the array of objects.
Try to play with add, remove, merge
http://backbonejs.org/#Collection-set.
I found the reason,
it was because I have attached an event on the model prototype
Backbone.Model.prototype.on('request',function(model, xhr, options){ ... });
Most likely is the Override issue,
even each record of the original JSON data is totally different at all.
Revoke idAttribute property in Model or revoke modelId method in Collection may help.
You could use .findWhere({_id: pkId}) instead of .get(pkId)

Are collections required?

Sorry this is a noob question but if I only need some initial data when the application first loads is a collection always needed or can the model fetch the data and pass it directly to the view?
Nothing in backbone is really "required". It's a very thin, more-than-one-way-to-do-it framework. Jeremy recommends data that can be bootstrapped in the initial page load be handled that way, so your HTML could include you initial data as JSON in a <script> tag. You can pass that JSON to a Backbone.Collection (if it's a list of similar records) or a new Backbone.Model (if it's a single domain object). You can also just use a model and call model.fetch to get your initial data. Model vs. Collection is more about single domain object with name/value pairs vs list of many objects where iterating, sorting, filtering are common.

How to generate model id's with Backbone.js

I am setting up the backbone sync mechanism and am a bit confused where to generate the id's for the models.
When I create a new model, should backbone be generating and setting the id, or am i supposed to implement an id generation method, or is there some sort of mechanism where I "PUT" the data to the server, which generates the id and returns a model with the id?
I'm providing a second answer to simplify the code you need to study to get the main points you're pondering about - the actual round about from model to server and how ids play their role.
Say you define a model - Let's go with Jurassic Park.
// Define your model
var Dinosaur = Backbone.Model.extend({
defaults: {
cavemanEater: undefined // Has one property, nom nom or not.
},
urlRoot: 'dino' // This urlRoot is where model can be saved or retrieved
});
var tRex = new Dinosaur({'cavemanEater':true});
You now have instantiated a dinosaur that is a meat eater. Roar.
console.log(tRex);
What you should notice is that in the properties of tRex, your model does not have an id. Instead, you will see a cID which you can think of as a temporary id that Backbone automatically assigns to your models. When a model doesn't have an id it is considered new. The concept of persisting a model (either to a database or local storage) is what allows you to go back to that resource after you've created it and do things like save (PUT) or destroy (DELETE). It would be hard to find that resource if you had no way to point directly at it again! In order to find that resource, your model needs an id, something it currently does not have.
So as the above answers have explained it is the job of your database (or localstorage, or some other solution) to provide Backbone with a resource id. Most of the time, this comes from the resource id itself, aka - the primary key id of your model in some table.
With my setup, I use PHP and mySQL. I would have a table called Dinosaur and each row would be a persistent representation of my dino model. So I'd have an id column (unique auto-incrementing int), and cavemanEater (bool).
The data communication flow happens like this.
You create a model.
The model is new so it only has a cID - no proper ID.
You save the model.
The json representation of your model is SENT to your server (POST)
Your server saves it to the table and gives it a resource id.
Your server SENDS BACK a json representation of the data {id:uniqueID}
Backbone RECEIVES this json representation with id
Backbone automagically updates your model with an id.
Here is what annotated code looks like.
CLIENT:
tRex.save();
// {'cavemanEater':true} is sent to my server
// It uses the urlRoot 'dino' as the URL to send. e.g. http://www.example.com/dino
SERVER:
// Is setup to accept POST requests on this specific ROUTE '/dino'
// Server parses the json into something it can work with, e.g. an associative array
// Server saves the data to the database. Our data has a new primary id of 1.
// Data is now persisted, and we use this state to get the new id of this dino.
$dinoArray = array('id'=>1, 'cavemanEater'=>true);
$dinoJSON = json_encode($dinoArray);
// Server does something to send $dinoJSON back.
CLIENT:
// If successful, receives this json with id and updates your model.
Now your tRex has an id = 1. Or should I say...
tRex.toJSON();
// RETURNS {'id':'1', 'cavemanEater':'true'}
Congrats. If you do this tRex.isNew() it will return false.
Backbone is smart. It knows to POST new models and PUT models that already have a resource id.
The next time you do this:
tRex.save();
Backbone will make a PUT request to the following URL.
http://www.example.com/dino/1
That is the default behavior by the way. But what you'll notice is that the URL is different than save. On the server you would need a route that accepts /dino/:id as opposed to /dino
It will use the /urlRoot/:id route pattern for your models by default unless you tweak it otherwise.
Unfortunately, dinosaurs are extinct.
tRex.destroy();
This will call... Can you guess? Yep. DELETE request to /dino/1.
Your server must distinguish between different requests to different routes in order for Backbone to work. There are several server side technologies that can do this.
Someone mentioned Sinatra if you're using Ruby. Like I said, I use PHP and I use SLIM PHP Framework. It is inspired by Sinatra so it's similar and I love it. The author writes some clean code. How these RESTful server implementations work is outside the scope of this discussion though.
I think this is the basic full travel of new Backbone data with no id, across the internets to your server where it generates, and sends back the resource id, to make your model live happily ever after. (Or destroy() not...)
I don't know if this is too beginner for you but hopefully it will help someone else who runs into this problem. Backbone is really fun to program with.
Other similar Answers:
Ways to save Backbone JS model data
or is there some sort of mechanism where I "PUT" the data to the server, which generates the id and returns a model with the id?
Kind of. When you call the save method of your model, backbone make a POST XHR and your application server should respond with JSON contains an id.
You can see an example here: http://addyosmani.com/blog/building-backbone-js-apps-with-ruby-sinatra-mongodb-and-haml/
Quoting from the link:
post '/api/:thing' do
# parse the post body of the content being posted, convert to a string, insert into
# the collection #thing and return the ObjectId as a string for reference
oid = DB.collection(params[:thing]).insert(JSON.parse(request.body.read.tos))
"{\"id\": \"#{oid.to_s}\"}"
end
If you don't know Ruby keep in mind what the last expression that is evaluated is automatically returned by the method.
What I understand from your question is that you want to have a collection with models that exist on the server. In order to get these models into the collection you'd have to add call 'fetch()' on the collection.
The url would be "/users" or something similar, that would have to return an array of objects with user data in there. Each item in the array would then be passed to UserCollection.add(). Well, actually it would be passed all at once, but you get the point.
After this your collection is populated. The url on the Model is meant for updating and saving the individual model. The url of the collection will also be used for creating models. Backbone's sync is RESTful, like Ruby on Rails. You can actually learn more about it on the documentation of Ruby on Rails:
http://guides.rubyonrails.org/routing.html
What you would generally do is have a different url for your model than for your controller. After populating your collection you have id's for each model in there because they came from the server.
Now when you add a new model based on user input you'd do something like this:
var HomeModel = Backbone.Model.extend({
defaults: {
lead: "not logged in",
},
url: 'test.php',
initialize: function(){
_.bindAll(this, 'handleSave', 'handleError');
// Save already knows if this.isNew.
this.save(undefined, {success: this.handleSave, error: this.handleError});
},
handleSave: function(model, response){
this.model.reset(model);
},
handleError: function(){
},
});
var HomeView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.model = new HomeModel();
this.model.bind("change", this.render);
},
el: 'div',
render: function() {
// Do things to render...
}
});
var homeView = new HomeView();
The example is from someone else's question I answered, I just add the relevant things.
The general idea is to save the model when it is created, if you need it somewhere else you can just move the code into a function of the model and call that based on events or anything else.

Resources