I am rolling a localStorage adaper for Ember data and when I run the find function, ie:
App.store.find(App.Person, 0 );
I am getting this error:
Uncaught Error: assertion failed: A data hash was loaded for a model of type App.Person but no primary key 'undefined' was provided.
At a more general level, I am a bit confused about the relationship between 'the persistent layer' ( in this case localStorage ) and the Ember Store. What does it mean to load something to the store? Does it mean an instance of DS.Person model is created with the data ?
Also, if you'll note I commented out a line 'App.store.didCreateRecord( model, data )' in the createRecord method since it, too is not working. What happens if I do not call store.didCreateRecord after the record has been loaded to localStorage.
The details:
DS.LocalStorageAdapter = DS.Adapter.extend({
get: Ember.get,
set: Ember.set,
createRecord: function( store, modelType, model ){
var records, index, data;
// get existing records of this model
records = this.localStorage.get( modelType );
index = records.length;
data = this.get( model, 'data' );
// set storageID of data
data.set( 'storageID', index );
// add data to existing records
records[index] = data;
// encode records
records = JSON.stringify( records );
// store records in localStorage
this.localStorage.set( modelType, records );
// App.store.didCreateRecord( model, data );
},
find: function( store, modelType, id ) {
var records, model;
records = this.localStorage.get( modelType );
model = records[id];
App.store.load( modelType, model );
},
localStorage: {
set: function( modelType, value ){
localStorage.setItem( modelType, value);
},
get: function( modelType ){
var record = localStorage.getItem(modelType);
record = JSON.parse(record) || [];
return record;
}
}
});
//application model
App.Person = DS.Model.extend({
name: DS.attr('string', {key: 'css_name'}),
storageID: DS.attr('number', {defaultValue: 0, key: 'storageID'}),
});
//create a couple of records
App.store.createRecord(App.Person, { ... } );
App.store.createRecord(App.Person, { ... } );
//try and find one record
App.store.find(App.Person, 0);
error thrown:
Uncaught Error: assertion failed: A data hash was loaded for a model of type App.Person but no primary key 'undefined' was provided. ember-latest.js:51
Ember.assert ember-latest.js:51
(anonymous function) ember-latest.js:131
DS.Store.Ember.Object.extend.load ember-data.js:1530
DS.LocalStorageAdapter.DS.Adapter.extend.find bookApp_1.js:111
DS.Store.Ember.Object.extend.findByClientId ember-data.js:1174
DS.Store.Ember.Object.extend.find ember-data.js:1140
findRecord webApplication.js:210
onclick
also please note this is similar to another question posted a bit ago:
Ember Data: A data hash was loaded ... but no primary key 'undefined' was provided
though the answer there seems to not really explain what is going on.
Can give example of loading Fixture data for Ember 1.0 pre2
App.store = DS.Store.create({
revision: 11,
adapter: 'DS.FixtureAdapter'
});
App.User = DS.Model.extend({
email: DS.attr('string'),
password: DS.attr('string')
});
App.User.FIXTURES = [
{
"id": "1",
"email": "test#mail.com",
"password": "password"
}
];
App.User.find(1).get('email')
Works for me.
Related
My model urlRoot:
urlRoot: function() {
if (this.id != null ) {
return 'notes/' + this.id;
} else return 'notes';
}
Function:
window.show_note = function (note_id) {
var note = new Memo.Models.Note([], { id: note_id });
note.fetch({
success: function (collection, note, response) {
var noteObj = collection.get("0");
var noteView = new Memo.Views.FullNote( {model: noteObj }, {flag: 0 } );
$('.content').html(noteView.render().el);
}
});}
{ id: note_id } - I post this to server to get note by id
I want to do 'set' or 'get' functions on model 'note' after note.fetch in a callback function - success, but only I have is error: 'Uncaught TypeError: note.set is not a function'.
If I do this way: 'var noteObj = collection.get("0");'
I get that I need but I still can`t use get or set.
You should set urlRoot to:
urlRoot: '/notes'
And backbone will figure out that it needs to add the id to the url. (docs)
Assuming Memo.Models.Note is a model and not a collection, the above snippet should be like this:
window.show_note = function(note_id) {
var note = new Memo.Models.Note({ id: note_id });
note.fetch({
success: function (model, response, options) {
var noteView = new Memo.Views.FullNote({
model: model
}, {flag: 0 });
$('.content').html(noteView.render().el);
}
});
};
Note the argument passed to new Memo.Models.Note. A backbone model constructor takes two arguments: attributes and options (docs) as opposed to a collection, which takes models and options (docs). So you'll want to add the hash with the id property as the first argument.
Also note the function signature of the success callback. For a model the success callback takes three arguments: model, response and options (docs). You'll be interested in the model argument because that is the fetched backbone model. response is the raw response data.
I hope my assumptions are right and this is the answer you are looking for.
I have a blog application where the API calls the users "Users", but my Ember model is "Author".
App.Author = DS.Model.extend({
name: DS.attr(),
posts: DS.hasMany('post', {async: true}),
email: DS.attr()
});
I map incoming JSON with an AuthorSerializer:
App.AuthorSerializer = DS.RESTSerializer.extend({
normalizePayload: function(type, payload) {
return this.translateRootKey('user', 'author', payload);
},
translateRootKey: function(server_word, client_word, payload) {
var response = {},
key = payload[server_word] ? client_word : Ember.String.pluralize(client_word),
value = payload[server_word] ? payload[server_word] : payload[Ember.String.pluralize(server_word)];
response[key] = value;
return response;
}
});
But I can't figure out how to change the root of my POST/PUT when I'm persisting to the server. I want my root to be "user" or "users" (depending on the situation). The server is currently getting this from Ember as its parameters:
{"author"=>{"name"=>"asdf", "email"=>"asdf"}, "user"=>{}}
How do I tell Ember to use "user/s" as my key name when talking to the server?
Example:
{"user"=>{"name"=>"asdf", "email"=>"asdf"}}
I think you are looking for the serializeIntoHash hook.
App.AuthorSerializer = DS.RESTSerializer.extend({
serializeIntoHash: function(hash, type, record, options) {
hash["user"] = this.serialize(record, options);
}
});
I'm new to Backbone, and I'm very confused about what's happening when I pass a JSON array (of objects) to a Backbone Collection.
I'm fetching some JSON from a spreadsheet hosted on Google Drive. I'm parsing that data, as the actual data that I want to use in my collection is deeply nested. In my parse function, if I log the length of my desired array, I get 157 (that's correct). I then pass that array into a Backbone Collection, and the length of my collection is 1 (incorrect). It's as though foo.bar.length = 157, but there is only one 'bar' in 'foo', so when I pass foo.bar into the collection, it takes foo.bar and not the contents of foo.bar! Very confused.
Code below...
var table = new TableView();
TableItem = Backbone.Model.extend(),
TableItemCollection = Backbone.Collection.extend( {
model : TableItem,
url : 'https://spreadsheets.google.com/feeds/list/0AjbU8ta9j916dFdjSVg3YkNPUUJnWkZSWjBDWmZab3c/1/public/basic?alt=json-in-script',
sync : function( method, model, options ) {
var params = _.extend( {
type: 'GET',
dataType: 'jsonp',
url: this.url,
processData: false
}, options );
return $.ajax( params );
},
parse : function( resp, xhr ) {
console.log( resp.feed.entry.length ); // THIS LOGS 157
return resp.feed.entry;
}
} ),
TableView = Backbone.View.extend( {
initialize : function ( options ) {
this.collection = new TableItemCollection();
this.collection.on( 'reset', this.parseResponse, this );
this.collection.fetch( {
reset : true,
success : function ( model, response, options ) {
console.log( 'OK' ); // THIS LOGS 'OK'
},
error : function ( model, response, options ) {
console.log( 'ERROR' );
}
} );
},
parseResponse : function () {
console.log( this.collection.length ); // THIS LOGS 1
}
} );
If you dump one of the items returned by Google Spreadsheets, you will see that the data is nested in multiple objects, something like this
{
"id":{"$t":"https://spreadsheets.google.com/feeds/list/..."},
"updated":{"$t":"2013-07-30T12:01:24.000Z"},
"category":[{"scheme":"...","term":"..."}],
"title":{"type":"text","$t":"ACIW"},
"content":{},
"link":[{"rel":"self","type":"application/atom+xml","href":"..."}]
}
In a Fiddle http://jsfiddle.net/nikoshr/kHBvY/
Note how the id property is wrapped in an object "id":{"$t":"https://spreadsheets.google.com/feeds/list/0AjbU8ta9j916dFdjSVg3YkNPUUJnWkZSWjBDWmZab3c/1/public/basic/cokwr"}
Backbone collections don't allow duplicates and duplicates are determined based on their id. All your items are considered duplicates and collapsed into one. If you remove the id or disambiguate it, you will get your 157 items. For example,
parse : function( resp, xhr ) {
var data = resp.feed.entry, i;
console.log(data.length); // THIS LOGS 157
for (i=data.length-1; i>=0; i--)
data[i].id = data[i].id['$t'];
return data;
}
http://jsfiddle.net/nikoshr/kHBvY/2/ for a demo
You probably will have to unwrap all the attributes to use them in a non hair pulling way.
I'm newbie to MongoDB and Backbone, so I try to understand them, but it is hard. I have a big, big problem: I cannot understand how to manipulate attributes in Backbone.Model to use in Views only what I need. More specific - I have a model:
window.User = Backbone.Model.extend({
urlRoot:"/user",
idAttribute: "_id",
defaults: {
_id: null,
name: "",
email: "foo#bar.baz"
}
});
window.UserCollection = Backbone.Collection.extend({
model: User,
url: "user/:id"
});
And I have a View:
beforeSave: function(){
var self = this;
var check = this.model.validateAll();
if (check.isValid === false) {
utils.displayValidationErrors(check.messages);
return false;
}
this.saveUser();
return false;
},
saveUser: function(){
var self = this;
console.log('before save');
this.model.save(null, {
success: function(model){
self.render();
app.navigate('user/' + model.id, false);
utils.showAlert('Success!', 'User saved successfully', 'alert-success');
},
error: function(){
utils.showAlert('Error', 'An error occurred while trying to save this item', 'alert-error');
}
});
}
I have to use 'put' method whit data from any fields except '_id', so it must be smth like:
{"name": "Foo", "email": "foo#bar.baz"}
But every time, doesn't depend on what I do it send
{**"_id": "5083e4a7f4c0c4e270000001"**, "name": "Foo", "email": "foo#bar.baz"}
and this error from server:
MongoError: cannot change _id of a document old:{ _id: ObjectId('5083e4a7f4c0c4e270000001'), name: "Foo" } new:{ _id:
"5083e4a7f4c0c4e270000001", name: "Bar", email: "foo#bar.baz" }
Github link: https://github.com/pruntoff/habo
Thanks in advance!
From looking at your mongo error, the problem is not with mongo, it is just doing what it's supposed to do. It had an object with _id of ObjectId type: ObjectId('xxx') and now you're trying to change that object to have an _id of a String type (_id: "5083e4a7f4c0c4e270000001") and that Mongo apparently does not like.
So, the question is: why did the object have an id of type ObjectId in the first place? How did you set it the first time? If you used some other method to initialize it (I'm guessing server side), you should set the id type to be a String so that it is the same as the one coming from your script library. If you want it to stay an ObjectId, you will need to convert the String coming from your script to an ObjectId before you save it to Mongo.
HTH.
MongoDB creates _id as an ObjectID, but doesn't retrieve _id as an ObjectID.
Whether this inconsistency is 'correct behaviour' or not, it is certainly an annoying surprise for most MongoDB users.
You can fix it with:
if ( this._id && ( typeof(this._id) === 'string' ) ) {
log('Fixing id')
this._id = mongodb.ObjectID.createFromHexString(this._id)
}
See MongoDB can't update document because _id is string, not ObjectId
For a id on a model in backbone, its just id and all lower cased. What if my Id on the server is called UserId. In the parse method for backbone, how do I change UserId to id and use the same names for all other properties?
For eg.
window.User = Backbone.Model.extend({
defaults:
{
UserId: 0, // <--can i just tell backbone that this is my id?
Name: '',
Age: 0
}
parse: function(response){
var model = response;
model.id = response.UserId;
return model;
}
});
Is there a better way to do this?
How about telling backbone model that my id is of type UserId.
You have to say Backbone what property is your id using the idAttribute in the model:
window.User = Backbone.Model.extend({
idAttribute: "UserId",
...
})
and everything works fine :). Backbone will create a id property for you and collections of this model will get() by your UserId.
Do it like so:
parse: function(response) {
var attrs = {};
attrs.id = response.UserId;
return attrs;
}
Parse has the responsibility to return an attributes hash, not a model. As such, you need only transform the response into an attributes hash versus a model as you are doing.
To set the id i would do as mauromartini says. To change any other property you don't need to create any private variables, response from the server is just an object so manipulate it and return it.
window.User = Backbone.Model.extend({
defaults: { UserId: 0 },
parse: function(response){
response.id = response.UserId;
return response;
}
});
you could also add the following before you return the object if you want to keep the model clean:
delete response.UserId;