Loading Ember Data hasMany relationships using links - ember.js

I'm having some trouble getting hasMany relationships to auto load (default or async) - I'm using the "links" attribute so i can use a custom url for children, and using a custom serializer to put the links attribute in since the server doesn't provide it - is this not supported? (using ember data 1 beta 6 and ember 1.3.2)
App.Bag = DS.Model.extend({
elements: DS.hasMany('element')
});
App.Element = DS.Model.extend({
name: DS.attr('string')
});
App.BagSerializer = DS.RESTSerializer.extend({
extractSingle: function(store, type, payload, id, requestType) {
payload.links = {"elements": "/bags/" + id + "/elements"};
return this._super(store, type, payload, id, requestType);
}
});
I'm able to load a Bag fine, but the elements array is never populated, I never see a call to the /bags/id/elements url. Am I doing something wrong?
Thanks!

How about if you specify the elements relationship is asyc? Like this:
App.Bag = DS.Model.extend({
elements: DS.hasMany('element', {async: true})
});

Related

ember data not saving foreign key, sent as null

My ember app is not sending my foreign key to the back-end.
I have a table called issues which is has a related table called categories
My model is:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
category_id: DS.belongsTo('category'),
description: DS.attr('string')
});
My route is:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(){
return this.store.findAll('issue');
},
actions: {
create: function(){
var issue = this.store.createRecord('issue');
issue.name = this.get('controller').get('newName');
issue.description = this.get('controller').get('newDescription');
issue.category_id = parseInt(this.get('controller').get('newCategory'));
//debugger;
console.log(issue);
issue.save();
},
...
other actions
...
}
}
});
the console.log from above looks like the category_id is getting set correctly:
category_id: 3
description: "foobar"
name: "test"
However my JSON payload that gets sent to the backend looks like:
{"issue":{"name":"test","description":"foobar","category_id":null}}
I tried stepping through by adding a custom serialiser in app/serializers/application.js
export default DS.RESTSerializer.extend({
...
serialize: function(snapshot,options){
console.debug('options='+options);
debugger;
var json = this._super(snapshot, options);;
return json;
}
...
});
But I got lost in all the super calling super indirection.
The snapshot.record has category_id: 3, but the json coming back from the this._super() call has category_id: null
options has includeID:true
Any clues will be much appreciated ...
Ember : 2.0.2
Ember Data : 2.0.0
Your model definition is wrong, when dealing with relationships you define them just as you would define any other attribute, there is no need to use _id.
export default DS.Model.extend({
name: DS.attr('string'),
category: DS.belongsTo('category'),
description: DS.attr('string')
});
As for the creation you should always use setters/getters when dealing with ember objects:
create: function() {
var issue = this.store.createRecord('issue', {
name: this.get('controller').get('newName'),
description: this.get('controller').get('newDescription'),
category: this.get('controller').get('newCategory') // assuming new category is a DS.Model instance of category
});
issue.save();
}
If you wish to stick to the syntax you have you would use issue.set('name', this.get('controller').get('newName')), from the looks of your code it seems you are going about this in the wrong way.
You should have a this.route('new') nested under your issues route, that way you wouldn't have to use the controller to store information.
You would simply set the model of the new route to:
model: function() {
return this.store.createRecord('issue');
}
Your template would make use of the input helpers like so:
{{input value=model.name}} and your action would just get the currentModel and call .save().

Embedded data from RestApi

My data comes from REST API like this:
customers:[
id:3,
name:"Joue",
currency:{
id:5
iso_code:"BDT"
}
]
My model:
App.Customer = DS.Model.extend({
name: DS.attr('string'),
currency: DS.attr('string')
});
i populated a select box with the availabe currencies and now i want to select by "id" 5.
Since currency is embedded and its interpreted as string i cant access it.
As far as i know embedded records are no longer supported in ember-data 1.0.
do i have to rewrite my REST Api and get rid of the relationships or there is a workaround .
You can just create a custom serializer for the data.
Using your data (slightly modified, since the json isn't valid, and I'm guessing that's just cause it was hand written?)
{
customers:[
{
id:3,
name:"Joue",
currency:{
id:5,
iso_code:"BDT"
}
}
]
}
Here's a serializer for that particular response type (read more about it here https://github.com/emberjs/data/blob/master/TRANSITION.md)
App.CustomerSerializer = DS.RESTSerializer.extend({
extractArray: function(store, type, payload, id, requestType) {
var customers = payload.customers,
currencies = [];
customers.forEach(function(cust) {
var currency = cust.currency;
delete cust.currency;
if(currency){
currencies.push(currency);
cust.currency = currency.id;
}
});
payload = { customers:customers, currencies: currencies };
return this._super(store, type, payload, id, requestType);
}
});
And your models defined with a relationship
App.Customer = DS.Model.extend({
name: DS.attr('string'),
currency: DS.belongsTo('currency')
});
App.Currency = DS.Model.extend({
iso_code: DS.attr('string')
});
Example:
http://emberjs.jsbin.com/OxIDiVU/535/edit
currency is not "embedded", it's just an object. Don't declare it as a string in your model:
currency: DS.attr()
You say you want to "select" by id--what do you actually want to do? You can access the properties of currency directly:
{{! handlebars }}
Your currency id is {{currency.id}}.
// Javascript
id = model.get('currency.id');
No need for additional complexity involving serializers or additional models. However, you need to be careful when changing currency id, since
model.set('currency.id', 6)
will not dirty the model and it won't save. You'll need to also incant
model.notifyPropertyChange('currency')

Ember Data: How do I know if a relationship was changed?

I'm writing a custom data adapter (doesn't talk to webserver) and I need to know whether hasMany relationship was changed or not to persist relationship data.
App.Note = DS.Model.extend({
content: attr('string'),
createdAt: attr('date'),
tags: DS.hasMany('tag', { async: true })
});
App.Tag = DS.Model.extend({
name: DS.attr('string'),
notes: DS.hasMany('note')
});
I need to implement adapter's updateRecord:
updateRecord: function(store, type, record) {
// record.changedAttributes() returns changed attributes but not relationships
}
In my case list of tags attached to note may change (via note.get('tags').addObject(..) / removeObject(..)) so I need to get a diff. What's the best way to do that? I can remove all tags in a database and insert new tags but that's not efficient at all

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 use a string as a primary key with Ember Data?

I have the following code:
Pult.Zone = DS.Model.extend({
name: DS.attr('string'),
authoritative: DS.attr('boolean'),
user_id: DS.attr('number'),
rulesets: DS.hasMany('Pult.Ruleset')
});
Pult.RESTAdapter.map('Pult.Zone', {
primaryKey: 'name',
rulesets: { key: 'rulesetIds' }
});
However, it doesn't seem like is picking up on the primary key correctly. I have rendered a list of all zones.
Here's a test case:
zones = Pult.store.findAll(Pult.Zone);
zones.get('length'); // Returns 10
zones = Pult.store.findAll(Pult.Zone);
zones.get('length'); // Returns 20
So every time I load zones from the server, it adds them to the local list, since it does not recognize them as already existing. Any way to fix this, or will I have to try to mock up some surrogate keys?
After upgrading to Ember Data 1.0.0 Beta 2, I found a solution that works:
App.Zone = DS.Model.extend({
name: DS.attr('string'),
user_id: DS.attr('number'),
});
App.ZoneSerializer = DS.RESTSerializer.extend({
normalize: function(type, hash, property) {
// Ember Data use the zone name as the ID.
hash.id = hash.name;
// Delegate to any type-specific normalizations.
return this._super(type, hash, property);
}
});