camelCased Model names - ember.js

does anyone know if there is a recent problem with camelcased properties.
I have a model like this:
var attr = DS.attr;
App.Users = DS.Model.extend({
firstName: attr('string'),
phone: attr('string'),
email: attr('string')
});
In my template email and phone show up correctly but firstName doesnt appear. I checked the json file and everything seems to be fine. With all my other models the same problems appear,
so i guess it has to do something with the camelCase.

Deprecation Notice: This answer is from 2013, and hasn't been touched up until now. This does not reflect JSON API Adapter but rather Ember-Data's RESTAdapter.
Ember.js relies on naming conventions, and it expects that multiple-word-camel-case (e.g. firstName) model properties to be mapped to multiple-word-underscore-separated-lower-case attributes (e.g. first_name) on the JSON response. If your JSON is giving you firstName, or anything that is not in this convention in a scenario that you do not control the backend API, you have the option of defining a map which tells your adapter to look for a specific key in the JSON response and map it to a property in a given Model.
You can do something like this:
DS.RESTAdapter.map('App.User', {
firstName: { key: 'firstName' },
lastName: { key: 'familyName' }
});
Note that in the sample above I've added a lastName property which is not part of your own model. I've done that just to make it clear that you can map several properties at the same time, and its name in the JSON response can be anything, not necessarily the same name in a different casing.

Related

Handle JSON Body After save() on a Model

In an application using the standard Ember 2.0 DS.JSONAPISerializer, the default behaviour for the REST API responding to a save() is "to return an empty object ({})."[1]
In my case, my REST API performs some changes to the data—including assigning a bigserial int64 ID from Postgres and processing text entry to Markdown (much, much faster than Ember can). This should (has to be) synchronous/blocking.
Is there an idiomatic approach to handling the response from save() on the default DS.JSONAPISerializer so that a (the) returned JSON model is stored in Ember's cache?
From what I can see I should be overriding normaliseSaveResponse to achieve this when accepting a response from the server. It's not immediately clear how I should set the fields of the "just saved" model with the server-provided values, however:
// app/models/item.js - simplified
export default DS.Model.extend({
id: DS.attr("number"),
title: DS.attr(),
body: DS.attr(),
// Only populated in the server response
rendered: DS.attr(),
createdDate: DS.attr("date"),
normalizeSaveResponse: function(store, primaryType, payload, id, requestType) {
// What goes here to deserialize the response into the model?
this.id = payload.item[i].id,
this.title = payload.item[i].title,
this.body = payload.item[i].rendered,
}
});
To sum:
I need to populate the model's ID field with a serial ID from the server as a response to save()
I also need to populate the rendered field with Markdown
Date fields, etc with server validated timestamps
Does this work when saving a single item AND multiple items?
If there's a better way to achieve this - e.g. a different method - I'm open to suggestions. The desire is to have additional validation/transforms happen on the server, return the 'corrected' response, and then have Ember save it in its own store => transition to /model/:id.
Just to close this out: Ember Data's default behaviour is to populate the fields (empty or not) in your model with the response JSON, provided your data has the correct structure for your adapter (i.e. RESTAdapter)
e.g.
A model like the below using the RESTAdapter:
export default DS.Model.extend({
id: DS.attr("number"),
title: DS.attr(),
body: DS.attr(),
// Only populated in the server response
rendered: DS.attr(),
// Only populated in the server response
createdDate: DS.attr("date"),
});
... will have the rendered and createdDate fields populated by the server provided your response looks like the below, with one exception (noted):
{
// This can be pluralized regardless of a single item or array response as Ember can deserialize it regardless
"posts": [{
"id": 1,
"title": "I'm Running to Reform the W3C's Tag",
"body": "Yehuda Katz",
"rendered": "<some markdown>",
// snake_case - not suitable by default
"created_at": "2015-09-08T20:31:09Z"
}]
}
You can fix this by overriding the keyForAttribute method on your serializer:
// app/serializers/application.js
export default DS.RESTSerializer.extend({
keyForAttribute: function(key) {
return Ember.String.decamelize(key);
}
});
Ref: http://emberigniter.com/fit-any-backend-into-ember-custom-adapters-serializers/

Ember Data Endpoint Issue

I am experiencing a weird issue while using ember data. With the following user model everything works great.
App.User= DS.Model.extend({
firstName: attr(),
lastName: attr()
});
I call user.save() and is posts to /users with the correct data. However when i try and use a user model that has relationships on it
App.User= DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
friends: DS.hasMany('user'),
followers: DS.hasMany('user'),
});
For some reason with that model when i call user.save() it posts to /Users (note the capitalization. Also, in the response it expects it formatted {"User": {...}} instead of {"user": {...}}
Anyone run into this before? I could always add the additional endpoints to my api however I would like it to work uniform if possible.
I did a little more digging and it seems when you add a relationship to the model there is a computed property called relationshipsByName. This property, in my example, will set the meta.type property to 'User'. It works without relationships because I called the createRecord method with 'user' so i assume it uses this as the type. When the relationship is added it uses 'User'
I found that modelFor calls the resolvers normalize on the keys. So the solution is to add a custom resolver like below.
App = Ember.Application.create({
Resolver: Ember.DefaultResolver.extend({
normalize: function(fullName) {
var n = this._super(fullName);
if(fullName.startsWith('model')){
n = n.replaceAt(6, n[6].toLowerCase());
}
return n;
}
})
});
*note i have string extensions for startsWith and replaceAt

Ember Data 1.0.0: what is expected format for belongsTo relationship

I have the following models:
App.Publication = DS.Model.extend({
title: DS.attr('string'),
bodytext: DS.attr('string'),
author: DS.belongsTo('author')
});
App.Author = DS.Model.extend({
name: DS.attr('string')
});
And the folowing json data:
{
"publications": [
{
id: '1',
title: 'first title',
bodytext: 'first body',
author_id: 100
},
{
id: '2',
title: 'second title',
bodytext: 'second post',
author_id: 200
}
];
}
In Ember Data RC12 this worked (you could specify author_id OR author in the json and the publication would always have the correct author linked).
In Ember Data 1.0.0 this no longer works; author is always null.
In some documents I found that - since I am using "author_id" in the json data (and not simply author) - I need to specify the key in the model; thus:
author: DS.belongsTo('author', { key: 'author_id' })
This however does not work; the author in the publication remains null.
The only solution I see for now is to implement a custom serializer and override the author_id to author (via normailzeId); I cannot change my backend data structure ... thus:
App.MySerializer = DS.RESTSerializer.extend({
//Custom serializer used for all models
normalizeId: function (hash) {
hash.author = hash.author_id;
delete hash.author_id;
return hash;
}
});
Is the above the correct way ?
Ember Data 1.0 no longer does any payload normalization by default. The key configuration for DS.belongsTo has been removed as well so you will have to implement a custom serializer.
normalizeId is an internal serializer function used for converting primary keys to always be available at id. You shouldn't override this.
Instead, you can override the keyForRelationship method which is provided for this purpose.
You could use something like the following:
App.ApplicationSerializer = DS.RESTSerializer.extend({
keyForRelationship: function(rel, kind) {
if (kind === 'belongsTo') {
var underscored = rel.underscore();
return underscored + "_id";
} else {
var singular = rel.singularize();
var underscored = singular.underscore();
return underscored + "_ids";
}
}
});
Note: I've also renamed the serializer to App.ApplicationSerializer so that it will be used as the default serializer for your application.
Finally, if you haven't already found it, please take a look at the transition notes here: https://github.com/emberjs/data/blob/master/TRANSITION.md
If you read through the transition document shortly after the initial 1.0.0.beta.1 release I would recommend taking a look again as there have been a number of additions, particularly regarding serialization.
From the Ember 1.0.0 Transition Guide:
Underscored Keys, _id and _ids
In 0.13, the REST Adapter automatically camelized incoming keys for you. It also expected belongsTo relationships to be listed under name_id and hasMany relationships to be listed under name_ids.
If your application returns json with underscored attributes and _id or _ids for relation, you can extend ActiveModelSerializer and all will work out of the box.
App.ApplicationSerializer = DS.ActiveModelSerializer.extend({});
Note: DS.ActiveModelSerializer is not to be confused with the ActiveModelSerializer gem that is part of Rails API project. A conventional Rails API project with produce underscored output and the DS.ActiveModelSerializer will perform the expected normalization behavior such as camelizing property keys in your JSON.

Customizing what Ember Data sends to the server?

Two of the models in my application are projects and users. Projects can have many users assigned to them and vice versa. I'm using Ember Data, so my model looks like this:
App.Project = DS.Model.extend({
name: DS.attr('string'),
created: DS.attr('date'),
users: DS.hasMany('App.User')
});
When creating a project on the server, the API expects to receive the project's name AND an array of IDs corresponding to the project's users. So, basically, something like this:
POST /projects
{
project: {
name: 'My Project',
users: [1, 10, 14]
}
}
However, Ember Data isn't including the array of user IDs when sending a POST or PUT request. By default, it only includes the name attribute. How can I modify Ember Data to include what I need? Is it even worth doing, or should I go the Discourse route and abandon Ember Data for now?
Assuming you are using the latest version and the RESTAdapter/RESTSerializer, you can override the addHasMany method of the serializer.
So, here is an example of how to do this:
App.CustomSerializer = DS.RESTSerializer.extend({
addHasMany: function(hash, record, key, relationship) {
var ids = record.get(relationship.key).map(function(item) {
return item.get('id');
});
hash[relationship.key] = ids;
},
});
App.Store = DS.Store.extend({
adapter: DS.RESTAdapter.extend({
serializer: App.CustomSerializer.create()
})
});
Note that the addHasMany implementation is taken from https://github.com/emberjs/data/blob/master/packages/ember-data/lib/serializers/fixture_serializer.js#L41

How to change Record on client directly, without transaction

I have a very common situation, if there is a term for it, well I am not aware of it then.
A record is having fields: id, enabled, text, etc...
and having POST /record/enable to enable or disable record, as it invoke is bigger process on server.
So, once callback from normal POST is received, I want to update record.enabled to true locally, which should not be part of any transaction. and should be updated directly.
How to achieve this?? Or what is better alternative for such requirement?
I think something along these lines should do:
App.PObject = DS.Model.extend({
id: DS.attr('number'),
name: DS.attr('string'),
nestedObject: function() {
if (!this.nestedObj) {
this.nestedObj = Ember.Object.create({
active: false
});
}
return this.nestedObj
}.property()
});
As i have seen so far, Model is dirty only when its attributes, that are defined as DS.attr, change.
EDIT: Realized later that, This solution doesn't work.
Proper term of my problem is: transient field
Transient field of an Object is ignored for serialization/deserialization (like Java ORMs ignores transient fields) http://en.wikipedia.org/wiki/Transient_(computer_programming
I have figured out a solution. Its really simple.
App.PObject = DS.Model.extend({
id: DS.attr('number'),
name: DS.attr('string'),
//will not be part of Ember-data persistence, but will be populated on GET
enabled: null
});
Object (Record) properties which are not attributes are ignored.