Get belongsTo ID without fetching record - ember.js

I'm trying to fetch the belongsTo ID without fetching the actual record. My JSON API returns the ID for the belongsTo relation.
I have the following models
App.Recipe = DS.Model.extend(
title: DS.attr()
ingredients: DS.hasMany('ingredient', async: true)
)
App.Ingredient = DS.Model.extend(
recipe: DS.belongsTo('recipe')
product: DS.belongsTo('product', async: true)
)
App.Product = DS.Model.extend(
title: DS.attr()
)
This is my route:
App.RecipeIndexRoute = Ember.Route.extend(
model: (args) ->
#store.find('recipe', args.recipe_id)
)
This is my controller:
I'm trying to find the product id within this controller.
App.RecipeIndexController = Ember.ObjectController.extend(
hasRecipeInCart: null
actions:
toggleCart: ->
#content.get('ingredients').then((ingredients) =>
# how do I get product id here, without lookup up the product relation?
# this 'get' makes Ember call: /api/v1/products/1
# I simply want the product ID (in this case 1), without having to call the server again.
ingredient.get('product').then((product) =>
product.id # => 1
)
My JSON looks like this:
HTTP GET: /api/v1/recipes/1
{
"recipe": {
"id":1,
"title":"Recipe 1",
"ingredients":[2,1]
}
}
HTTP GET: /api/v1/ingredients?ids[]=2&ids[]=1
{
"ingredients": [
{
"id":2,
"recipe":1,
"product":1
},
{
"id":1,
"recipe":1,
"product":2
}
]
}

This is now much easier with the ds-reference feature that was added to ember data. You can now do:
var authorId = post.belongsTo("author").id();
See http://emberjs.com/blog/2016/05/03/ember-data-2-5-released.html#toc_code-ds-references-code

A ton of work is going into redoing the relationships, you can pull it out of the underlying properties from the data attribute model.get('data.product.id')
Example: http://emberjs.jsbin.com/OxIDiVU/16/edit

For ED 2.x, relationships have been reworked and were broken for getting underlying data until the ds-references feature landed.
To do this in ED 2.4+, you need to use the new belongsTo method to work with underlying data.
To get an id for a belongsTo ref:
var id = this.get('model').belongsTo('myBelongsToKey').value();

Related

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.js get compound data for each item

I'm trying to build the following view with Ember.js:
Users: (x in total)
* User 1: y Posts
* User 2: z Posts
I've created a itemController that is responsible for getting the number of posts of each user.
App.IndexItemController = Ember.ObjectController.extend({
postCount: function() {
var posts = this.get('content').get('posts');
return posts.get('length');
}.property()
});
Full code on jsbin.
Somehow I always get 0 posts for each user, I guess that is because the relationship is not resolved correctly at this.get('content').get('posts'). What would be the right way to do this? Or am I going a completely wrong way?
Bonus question: What can I pass to the property() and should I pass something to it?
You need to set the dependent keys of your computed property, in your case content.posts.length. So the postCount knows when need to be updated.
App.IndexItemController = Ember.ObjectController.extend({
postCount: function() {
var posts = this.get('content').get('posts');
return posts.get('length');
}.property('content.posts.length')
});
Now your computed property is correct, but no data is loaded, this happen because there isn't posts associated with your users, no in the user -> post direction. So you need to add it in the fixture:
App.User.FIXTURES = [
{
id: 1,
name: 'Jon',
nick: 'Jonny',
posts: [1]
},
{
id: 2,
name: 'Foo',
nick: 'Bar',
posts: [2]
}
];
After this an error is raised Uncaught Error: Assertion Failed: You looked up the 'posts' relationship on '<App.User:ember280:1>' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`).
Ember data identified that you have an async relationship, and warns you to setup the property with async: true
App.User = DS.Model.extend({
name: DS.attr('string'),
nick: DS.attr('string'),
posts: DS.hasMany('post', { async: true })
});
This your updated jsbin

How to submit a form with related model data in ember-data 1.0?

I'm creating a record from both form data and another promise (for the related record).
Here is the basic JSON I'm centered around
//appointment
{
"id": 6,
"details": "test",
"customer": 1
}
//customer
{
"id": 1,
"name": "Sle eep",
"appointments": [6]
}
My ember-data models look like this
App.Appointment = DS.Model.extend({
details: attr('string'),
customer: belongsTo('customer', { async: true})
});
App.Customer = DS.Model.extend({
name: attr('string'),
appointments: hasMany()
});
When I create an appointment it currently looks something like this
this.store.find('customer', 1).then(function(customer) {
var appointment = {
details: 'foo',
customer: customer.get('id')
}
this.store.createRecord('appointment', appointment).save();
});
The problem with the above is that my serializer doesn't do well when the form data is a promise. Is this how I should be creating records? If not, what should this create look like?
Thank you in advance
Update
After a little more looking around, it seems the async: true on belongsTo might be the issue. Just before the "createRecord" I can see the following
Object {details: "asd", customer: 1}
But when I jump into the "createRecord" method of ember-data (in the RESTAdapter) I notice that now customer is represented as a promise again (not the integer value or string value I saw just before that method was invoked)
Why don't you wait for the find to be resolved before creating the record and sending?
var self = this,
promise = this.store.find('customer', 1); //just to show it's a promise
promise.then(function(customer){
var appointment = {
details: 'foo',
customer: customer
}
self.store.createRecord('appointment', appointment).save();
},{
alert("uh oh end of the world, couldn't find customer");
});
Async isn't extremely well documented and seems to have some willy-nilly side-effects (It is all still in beta, so no major judgement from me yet). That being said, your appointments hasMany isn't defined, here's a playground for this.
http://emberjs.jsbin.com/OlaRagux/5/edit

How to rollback changes to HasMany in ember-data

I have following models:
App.Parent = DS.Model.extend({
foo: DS.attr('string'),
children: DS.hasMany('child', {async: true})
});
App.Child = DS.Model.extend({
bar: DS.attr('string')
});
I filled them with some fixture data:
App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.Parent.FIXTURES = [
{
id:0,
foo: 'parent',
children: [0,1]
}
];
App.Child.FIXTURES = [
{
id: 0,
bar: 'child 0'
},
{
id: 1,
bar: 'child 1'
},
{
id: 2,
bar: 'child 2'
}
];
After I do some changes to the children relation, how can I rollback children relation to its latest saved state?
I push new child to manyArray in the following way:
this.store.find('child', 2).then(function(child){
model.get('children').pushObject(child);
})
This does change the relation (I see new child in my view), but parent record does not become dirty. Thus, when I try model.rollback() it does nothing. I also tried solution I found here How to rollback relationship changes in EmberData which is adding model.send('becomeDirty') before rollback, but it does not help.
Maybe I am adding children to my relation in a wrong way?
Thanks!
I used this to rollback related records that are dirty.
App.ParentRoute = Ember.Route.extend
model: ->
#get('store').createRecord('parent', child: #get('store').createRecord('child'))
actions:
willTransition: (transition) ->
rollbackRecords(#)
rollbackRecords = (context) ->
if context.get("controller.content.isDirty")
relationships = Ember.get(App[context.get('controller').resourceName], "relationshipsByName")
content = context.get('controller.content')
relationships.forEach (name, relationship) ->
relatedModel = content.get(name)
relatedModel.rollback() if relatedModel? and relatedModel.get('isDirty')
content.rollback()
true
Here is some code that will rollback a model, as well as it's relationships that I use:
var model = this.get('model');
var relatedModel, relatedModels;
model.constructor.eachRelationship(function (key, relationship) {
if (relationship.kind === 'hasMany') {
relatedModels = model.get(key);
if (relatedModels) {
relatedModels.invoke('rollback'); //since this is an array, need to call the rollback function differently
}
}
else {
relatedModel = model.get(key);
if (relatedModel) {
relatedModel.rollback();
}
}
});
model.rollback();
Hope this helps
I believe the other answers listed here only partially solve this problem. If you add a new related model or remove an existing one, rollback should reverse those as well and I believe the other answers don't address this. Here is a complete solution that provides proper dirty checking and a complete rollback for hasMany and belongsTo relationships:
https://stackoverflow.com/a/27184207/188740
I like to do the good ol' model.reload()
- it'll blow away all changes you haven't synced with your server though.

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.