model's fetch does not work - backbone.js

Here is body of my model
urlRoot: '/users',
parse: function(response) {
return response.User;
}
Here is what is returned when I type /users/1 in my browser:
{"User":{"id":"1","created":"2013-02-13 09:22:42","modified":"2013-02-13 09:22:42","username":"somesuername","password":"","role":"somerole","token":null,"token_expiration":null}}
So the api works.
When I execute this:
this.model.id = 1;
this.model.fetch({ success: function(user){
console.log(user);
}}); // this.model is instance of my model
I get in console:
d {cid: "c4", attributes: Object, _changing: false, _previousAttributes: Object, changed: Object…}
_changing: false
_pending: false
_previousAttributes: Object
attributes: Object
__proto__: Object
changed: Object
cid: "c4"
id: Array[1]
__proto__: e
So the result was successful by model didn't fetch any data - am I missing something?

Backbone.Model stores the data inside the attributes property. From Backbone documentation:
The attributes property is the internal hash containing the model's state — usually (but not necessarily) a form of the JSON object representing the model data on the server. It's often a straightforward serialization of a row from the database, but it could also be client-side computed state.
Please use set to update the attributes instead of modifying them directly.
Try expanding the attributes node in the console object inspector, and you should see your data.

Related

Collection fetch returns one model but response has all models

I'm new to Backbone and on fetching a collection, I can see the server return all 15 collections. The fetch success returns all 15 models in the response object but the collection object has only the last of the 15 models.
var BracketModel = Backbone.Model.extend({
defaults: {
id: '',
name: '',
title: ''
},
urlRoot: 'http://test.com/bracket/rest.php',
.....
}),
var BracketsCollection = Backbone.Collection.extend({
url: 'http://test.com/bracket/rest.php?op=list',
model: BracketModel,
}),
bracketCollection.fetch({
success: function (collection, response) {
// Collection.models only has one model, response has 15
var bracketsView = new BracketsView({collection: collection});
},
Try
var bracketsView = new BracketsView({collection: response});
Or
var bracketsView = new BracketsView({collection: collection.toJSON()});
I haven't tested it now, but if I remember well, both are equivalent.
The first parameter returns the collection object, which gives you access to different collection attributes. The second parameter returns 'an array containing the attributes hash of each model in the collection', which is likely the thing you are looking for.
The pattern that I usually go with for passing a collection to a view goes like this:
var bracketCollection = new BracketsCollection();
var view = new brackatsView({collection: bracketCollection});
brackCollection.fetch();
Then inside of your view's initialization method do this:
this.listenTo(this.collection, 'sync', this.render);
What this all is doing is creating your collection and your view, and then when you create the view you are telling it about the collection. Calling fetch on the collection is an asynchronous event that will fire a 'sync' even when it is done. The view will listen for this sync event, and when it happens will call the render function.

How to get nested object's attributes from model.toJSON

I am learning Backbone.js. I have a model call myModel, and here is the console.log result when I call myModel.toJSON():
Object {accountId: "523f628e80d52a2805000004", added: "2013-09-26T06:26:12.765Z", updated: "2013-09-26T06:26:12.765Z", _id: "5243d384951b6cef05000004", name: Object}
I want to get the attributes in name, when I console.log myModel.toJSON().name, I got this result:
Object {first: "myfirst", last: "mylast"}
Everything works fine, until I call myModel.toJSON().name.first, I got a console error says
Cannot read property 'first' of undefined
I thought myModel.toJSON().name is an object, and to get the 'first' attribute from it, I just need to do myModel.toJSON().name.first. Any thought why it doesn't work?
This is the entire model I have:
r {cid: "c71", attributes: Object, collection: r, _changing: false, _previousAttributes: Object…}
_changing: false
_events: Object
_pending: false
_previousAttributes: Object
attributes: Object
_id: "5243d384951b6cef05000004"
accountId: "523f628e80d52a2805000004"
added: "2013-09-26T06:26:12.765Z"
name: Object
first: "myfirst"
last: "mylast"
__proto__: Object
updated: "2013-09-26T06:26:12.765Z"
__proto__: Object
changed: Object
cid: "c71"
collection: r
__proto__: s

Backbone+Parse.com Collection.fetch() returns empty array using event callback

i'm starting using parse.com to develop a web app but i'm stuck on a simple problem.
I defined a model (or object in Parse SDK) as:
Book.Model = Parse.Object.extend("book", {
// Default attributes for the book.
defaults: {
title: "placeholder...",
},
// Ensure that each book created has `title`.
initialize: function() {
if (!this.get("title")) {
this.set({"title": this.defaults.title});
}
},
});
and a collection:
Book.List = Parse.Collection.extend({
// Reference to this collection's model.
model: Book.Model,
initialize: function() {
},
});
Then, if i try something like
var books = new Book.List();
books.fetch({
success: function(collection) {
console.warn(collection);
},
error: function(collection, error) {
// The collection could not be retrieved.
}
});
Everything goes fine. Log:
child {length: 5, models: Array[5], _byId: Object, _byCid: Object, model: function…}
_byCid: Object
_byId: Object
length: 5
models: Array[5]
__proto__: EmptyConstructor
BUT if i try to use event callback instead of success method i get an empty array. Code:
books.on('reset', this.log());
books.fetch();
log: function() {
console.log(books);
}
and log:
child {length: 0, models: Array[0], _byId: Object, _byCid: Object, model: function…}
_byCid: Object
_byId: Object
length: 5
models: Array[5]
__proto__: EmptyConstructor
which is quite strange (because i think that each solution wait for the collection to be populated from the server). Does anybody know why is this happening?
I'm actually using Backbone Boilerplate and Parse.com js SDK.
The Collection#fetch behavior has changed, it used to reset the collection by default but as of 1.0.0 it merges the new models using set:
When the model data returns from the server, it uses set to (intelligently) merge the fetched models, unless you pass {reset: true}, [...]
and set doesn't trigger "reset" events, it triggers other events:
All of the appropriate "add", "remove", and "change" events are fired as this happens.
If you want your fetch to reset the collection then you have to say so:
books.fetch({ reset: true });

get not retrieving name attribute from object

From the backbone docs
getmodel.get(attribute)
Get the current value of an attribute from the model. For example: note.get("title")
I seeded some data with a name for each country into a Rails app
Country.create!(name: "Brazil")
Country.create!(name: "France")
Country.create!(name: "Germany")
Country.create!(name: "Spain")
In the console, I created a new collection and fetched the data
countries.fetch();
Object
XHR finished loading: "http://localhost:3000/countries".
Checked the length. Equal to the four countries I created.
countries.length
4
Selected a random one using underscore's shuffle method.
c = countries.shuffle()[0]
The object has a name
Backbone.Model
_changes: Array[2]
_currentAttributes: Object
_events: Object
_hasComputed: false
_previousAttributes: Object
attributes: Object
country: Object
created_at: "2013-01-07T06:09:43Z"
id: 2
name: "France"
updated_at: "2013-01-07T06:09:43Z"
__proto__: Object
__proto__: Object
Tried to get the name attribute from the object several different ways, all without success
c.get('name')
undefined
c.get("name");
undefined
c.get("name")
undefined
Can anyone think what I might be doing wrong?
One thing that I find unusual is that there is a 'country' attribute wrapping the other attributes
attributes: Object
country: Object
created_at: "2013-01-07T06:09:43Z"
id: 2
name: "France"
updated_at: "2013-01-07T06:09:43Z"
I'm not sure why 'country' is wrapping the other data. Is it because I created the seed data with a Country model?
Country.create!(name: "Brazil")
anyways, obj.get('country') doesn't return undefined
c = countries.shuffle()[0]
k = c.get('country')
Object
created_at: "2013-01-07T06:09:43Z"
id: 3
name: "Germany"
updated_at: "2013-01-07T06:09:43Z"
__proto__: Object
but I can't do a get on the name after that (i.e after I did the get on country)
k = c.get('country')
Object
k.get('name')
undefined
Is there a way to remove the country wrapper and just be able to do get on the attributes that it was created with?
Override model.parse to extract the country element. Backbone calls parse before populating the model from fetched data:
var Country = Backbone.Model.extend({
parse: function(attributes) {
return attributes.country;
}
});

backbone model saving, validation fails

no amount of Googling is managing to solve my confusion so I thought I'd ask the question on here.
I'm trying to save a model and make use of success/error callbacks. On the backbone documentation it states you save your model like so: model.save([attributes], [options]).
I cannot find anywhere on the documentation that tells you how to save the entire model (i.e. without specifying the attributes), but have come across this question where the second answer says to save the entire model you can do model.save({}, [options]).
However I am trying this to no avail. My code is below:
Backbone Model:
class Student extends Backbone.Model
url: ->
'/students' + (if #isNew() then '' else '/' + #id)
validation:
first_name:
required: true
last_name:
required: true
email:
required: true
pattern: 'email'
schema: ->
first_name:
type: "Text"
title: "First Name"
last_name:
type: "Text"
title: "Last Name"
email:
type: "Text"
title: "Email"
In my view I have the following function:
class Students extends CPP.Views.Base
...
saveModel = ->
console.log "model before", #model.validate()
console.log "model attrs", #model.attributes
#model.save {},
wait: true
success: (model, response) ->
notify "success", "Updated Profile"
error: (model, response) =>
console.log "model after", #model.validate()
console.log "model after is valid", #model.isValid()
console.log "response", response
notify "error", "Couldn't Update"
In the first console.log before the save I am told that the model is valid, via the means of an undefined response. If indeed I look at the model I can see that all three fields are filled in.
Similarly in the next two console logs in the error #model.validate() and #model.isValid() both return undefined and true respectively.
However the response I get from trying to save the model is Object {first_name: "First name is required", last_name: "Last name is required", email: "Email is required"}
Finally in the console.log of the models attributes I get:
Object
created_at: "2012-12-29 23:14:54"
email: "email#email.com"
first_name: "John"
id: 2
last_name: "Doe"
type: "Student"
updated_at: "2012-12-30 09:25:01"
__proto__: Object
This leads me to believe that when I passed {} to my model it was actually trying to save the attributes as nil, else why else would it have errored?
Could someone kindly point out what I'm doing wrong? I'd rather not have to pass each attribute individually to the save!
Thanks in advance
From the suggested answer by Hui Zheng I modified my controller in my server to return the student in JSON format.
However to find the real source of the problem I read the backbone documentation on saving, and found out that when wait: true is given as an option it performs the following:
if (!done && options.wait) {
this.clear(silentOptions);
this.set(current, silentOptions);
}
On further investigation into clear I found
clear: function(options) {
var attrs = {};
for (var key in this.attributes) attrs[key] = void 0;
return this.set(attrs, _.extend({}, options, {unset: true}));
},
From this it looks as if every attribute is being cleared to then be reset. However on clearing my model the validations I wrote will fail (since first_name, last_name, email are required).
On the backbone.validation documentation we are told that we can use the parameter forceUpdate: true, so I have opted to use this when saving my model. I'm going to assume for now (although this might not be good practice) that the data from the server is correct since this has been validated too.
Therefore my final code is:
saveModel = ->
#model.save {},
wait: true
forceUpdate: true
success: (model, response) ->
notify "success", "Updated Profile"
error: (model, response) ->
notify "error", "Couldn't Update"
Are you sure that the model's attributes have been set correctly before save? Even none of its attributes has been set, it may still pass validate(depending on how validate function is defined). Please try to print the model in the console to verify that. BTW, it's better to pass null instead of {} in save, this way the model's set method won't be invoked.
Updated:
According to the Backbone's source code, if passing null as the first argument of save, the model's attributes will be kept intact until the model has been saved on the server successfully. So the other possibility is that your server has succeeded in saving the model but returned a corrupted object, resulting in failure in model's set method. If you still cannot fix the problem, tracing the model.set method might help。

Resources