Ember Data 1.0.0: Model name capitalized in URL - ember.js

I am transitioning from Ember Data 0.13 to Ember Data 1.0 (beta 1). It seems that the URL constructed for a model is capitalized when it shouldn't. In ED 0.13, capitalization and pluralization occurred automatically and without problems. I suppose the same is still true in ED 1.0, but I must be overlooking something.
App.Account = DS.Model.extend({
// Attributes
company: DS.attr('string'),
// Relationships
users: DS.hasMany('User')
});
App.AccountAdapter = DS.RESTAdapter.extend({
namespace: 'api',
});
In the controller, I create a new record, populate it, and save it.
var account = this.store.createRecord('account');
account.set('company', this.get('company'));
account.save();
The request URL that Ember Data uses for saving the record is http://localhost:3000/api/Accounts. Why is the plural of the model name capitalized? How do I configure the model/adapter to use accounts instead of Accounts?

It appears that a change in the naming convention of model associations is the cause of this issue. Before ED 1.0, an association was declared as shown below.
App.Account = DS.Model.extend({
// Attributes
company: DS.attr('string'),
// Relationships
users: DS.hasMany('App.User')
});
In ED 1.0, however, there is no need to use App.User to declare an association. Passing the model's name, user suffices. Because I capitalized the name of the Account model in the User model, ED capitalized the name (plural) of the model in the URL.
App.User = DS.Model.extend({
// Attributes
first_name: DS.attr('string'),
last_name: DS.attr('string'),
email: DS.attr('string'),
password: DS.attr('string'),
password_confirmation: DS.attr('string'),
// Relationships
account: DS.belongsTo('account') // model name should be lowercase
});
Just like you need to pass the lowercase name of the model for creating a new record (this.store.createRecord('user');), you also need to use the lowercase model name to specify the model for an association.

Related

How to efficiently specify nested relationships in ember-data?

I have a nested model as such:
var School = DS.Model.extend({
classrooms: DS.hasMany('classroom', {async: true})
});
var Classroom = DS.Model.extend({
students: DS.hasMany('student', {async: true}),
school: DS.belongsTo('school', {async: true})
});
var Student = DS.Model.extend({
name: DS.attr('string'),
classroom: DS.belongsTo('classroom', {async: true})
});
I am using firebase as a backend, and I understand it is advisable to denormalize the schema for efficiency sake. Is there any utility in explicitly specifying the relationship
var Student = DS.Model.extend({
school: DS.belongsTo('school', {async: true});
});
for the Student model, even though this is implied by each Student belonging to a Classroom and each Classroom belonging to a School?
No. You shouldn't need to provide nested relationship information unless model in question is directly accessible from the root object (hope that makes sense).
The only "benefit" of doing so is that in your case the student model will be loaded when the school data is being loaded rather than waiting until the classroom data is loaded. However, this will provide a data structure that is not reflective of your intentions and so I wouldn't advocate doing this.
I would use a computed property or a binding to get what you're after - something like
school: Ember.computed.alias('classroom.school')
schoolBinding: 'classroom.school'
It wouldn't be a DS.belongsTo because the data wouldn't actually contain a school property

Ember data: reuse one model for two guises

In the app user has two associations - billing address and company address which are identical
//address
export default DS.Model.extend({
email: DS.attr('string'),
country: DS.attr('string'),
zip: DS.attr('string'),
user: DS.belongsTo('user')
//...
});
//user
export default DS.Model.extend({
name: DS.attr('string'),
company: DS.belongsTo('address'),
billing: DS.belongsTo('address')
//...
});
How to pull this kind of trick?
What I would do is use a Polymorphic relationship. The steps would be (I'm assuming ember-cli, if not the gist is the same):
ember generate model address
ember generate model company-address
ember generate model billing-address
In models/address.js you define the actual address model:
export default DS.Model.extend({
email: DS.attr('string'),
country: DS.attr('string'),
zip: DS.attr('string'),
user: DS.belongsTo('user')
//...
});
Then in the other models you extend from the parent model (polymorphism saves the day!).
company-address
import Address from './address';
export default Address.extend({
// Yeah, its empty.
});
billing-address
import Address from './address';
export default Address.extend({
// But, you could add custom fields here and
// still share the ones with the parent
// Polymorphism.
});
Now in your user model you do this:
export default DS.Model.extend({
name: DS.attr('string'),
company: DS.belongsTo('address', {polymorphic: true}),
billing: DS.belongsTo('address', {polymorphic: true})
});
There is no free lunch though, your JSON has to change its format now:
{ name: 'foobar',
company: {
// company fields or id if async
type: 'company-address'
},
billing: {
// billing fields or id if async
type: 'billing-address'
}
}
You are looking for the inverse options.
You need two fields on the Address model, one referring to the company with this address, one referring to the user with this address. Then, specify which is which with the inverse option.
//address
export default DS.Model.extend({
...
billing: DS.belongsTo('user', { inverse: 'billing' })
company: DS.belongsTo('user', { inverse: 'company' })
});
//user
export default DS.Model.extend({
...
company: DS.belongsTo('address'),
billing: DS.belongsTo('address')
});
See http://emberjs.com/guides/models/defining-models#toc_explicit-inverses.
For what you are trying to do, there is no reason whatsoever to define two separate derived models simply in order to get the associations set up properly. inverse does exactly what you need.

Multiple relationship between two objects

How do I define multiple relationships between two objects in Ember? For example A user can be either a staff or a customer the differentiator is the userType property. If a customer buys a product, the product object need to have link to the customer that bought it and the staff that facilitated the sale.
Here is a basic version of a data model that would suffice for what you have described. You can adapt it for your needs. I have used the app global haven't specified whether you are using ember-cli.
App.User = DS.Model.extend({
name: DS.attr('string'),
userType: DS.attr('string')
});
App.Order = DS.Model.extend({
orderedBy: DS.belongsTo('user'),
facilitatedBy: DS.belongsTo('user')
});
App.Product = DS.Model.extend({
name: DS.attr('string')
});
A useful tool for creating a first pass of a data model when you are unsure of the ember syntax is Ember Data Model Maker. You can use it to see how you should set up your model definitions and then modify them later.

Don't load a relationship

Lets say I have a model like this:
App.User = DS.Model.extend({
attributes : DS.attr('string'),
countries : DS.hasMany('country', { async: true }),
)};
And the server returns the JSON, with a country_idsarray which all works fine, but I don't want to actually load the countries models corresponding to these IDs, which ember-data is doing automatically. Is there any way to stop/suppress this automatic functionality?
Ember Data should load models only if you address to countries field.
If you need only ids try to define country_ids field in model.
UPD:
It seems like *_ids is reserved by framework. So you can ask your server to send this array with another name and define simple attr for this in model.
If you use rails and ActiveModel::Serializer it can be done like this:
class UserSerializer < ActiveModel::Serializer
embed :ids
attributes :id, :attributes
has_many :countries, key: :countries
end
And model:
App.User = DS.Model.extend({
attributes : DS.attr('string'),
countries : DS.attr()
)};

Ember-data lazy load association with "links" attribute

I have a model Teacher which has many Students. The models are defined as follows:
App.Teacher = DS.Model.extend({
email: DS.attr('string'),
students: DS.hasMany('student')
});
App.Student = DS.Model.extend({
teacher: DS.belongsTo('teacher'),
});
When a Teacher logs in, the server returns a JSON representation of the Teacher:
{
id: 1,
email: "abc#example.com",
links: {
students: /teacher/1/students
}
}
In the controller for login, I then push this data into the store and store it in a property of the Session controller:
this.set('currentUser', this.get('store').push('teacher', teacherJson))
I want to lazy-load the students association so I used the "links" format as defined in the API (http://emberjs.com/api/data/classes/DS.Store.html#method_push). So, ideally, whenever I would call
App.SessionController.get('currentUser').get('students')
it would load the associated students by sending a GET request to /teacher/1/students. But that never happens. Why is the request not triggered?
Ok, I found the answer. I had to add a property async: true to the students association in the model for Teacher:
App.Teacher = DS.Model.extend({
email: DS.attr('string'),
students: DS.hasMany('student', { async: true })
});