Finding record by custom primaryKey - ember.js

My model is defined as follow:
App.User = DS.Model.extend({
primaryKey: 'username',
username: DS.attr('string'),
name: DS.attr('string')
});
My custom Adapter map:
DS.SocketAdapter.map('App.User', {
primaryKey: 'username',
username: DS.attr('string'),
});
I am testing this model out by typing on console:
App.User.createRecord({username:"user_1"});
var r = App.User.find("user_1");
console.log( r.serialize() );
>> Object {username: null, name: null ..... all null}
But it retuns a "null" Object. Also tested:
App.User.find({username:"user_1"});
But this is doing a remote request. I read that Ember Data does allow you to find records via attributes other than the ID.
So what I am doing wrong in telling Ember data my custom primaryKey?

I think your problem lies in the fact that you are defining username twice. if you map username from your json to your model's primaryKey trough your Adapter, then you should avoid to do the same on the model I guess. There are different approaches where to define the mapping, but the Adapter is the most appropriate place In your case, see here for more details: https://github.com/emberjs/data/blob/master/BREAKING_CHANGES.md#mapping
change your code like so and it should work:
// Model
App.User = DS.Model.extend({
name: DS.attr('string')
});
// Adapter
DS.SocketAdapter.map('App.User', {
primaryKey: 'username'
});
now try to create a new record
App.User.createRecord({username:"user_1", name:"foo"});
and then find the record by it's id as you already did:
var r = App.User.find("user_1");
this
console.log( r.serialize() );
should then give you at least:
>> Object {name: "foo" ...}
hope it helps

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().

Ember data embedded has one relationship for ember-data.1.0.0

I am trying to serialize a user object and pass it to an ember client. My app uses the RESTserializer.
A user object has_one address object. There is no separate endpoint for just the address so I can't just provide a foreign key id and sideload it.
The JSON received just includes address as an object and looks something like this:
{"user":
{
"id":"5",
"name":"Andrew",
"address":
{
"id":"3",
"addressable_id":"5",
"street":"1",
"country_code":"US"
}
}
On the ember side I have a user model
App.User = DS.Model.extend({
name: DS.attr('string'),
address: DS.belongsTo('address'),
//hasOne via belongsTo as suggested by another SO post:
//http://stackoverflow.com/questions/14686253/how-to-have-hasone-relation-with-embedded-always-relation
});
and an address model
App.Address = DS.Model.extend({
addressable_id: DS.attr('string'),
street: DS.attr('string'),
country_code: DS.attr('string'),
user: DS.belongsTo('user')
});
Currently running this code throw an error in the console:
TypeError: Cannot read property 'typeKey' of undefined
which can be fixed by removing the
address: DS.belongsTo('address'),
line in the user model but then the relationship doesn't load properly.
So what am I doing wrong configuring this? I am having a hell of a time finding up to date documentation on this.
You need to use the DS.EmbeddedRecordsMixin on a per-type serializer.
In your case, you would need to do the following :
App.UserSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
address: {embedded: 'always'}
}
});
as explained in this excellent answer.

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

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);
}
});

Add extra url params per model with Ember.js

I have two models:
App.Providers = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
logo: DS.attr('string'),
products: DS.hasMany('App.Products')
});
App.Products = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string')
provider: DS.belongsTo('App.Providers'),
});
They are both using the same Adapter. However, for the Products model I want to append an extra url param (the api key) to the url. How can I extend the adapter (or the serializer?) to implement this?
So just to give you an example when I want to do a GET for providers:
http://example.com/ap1/v1/providers/
and for products:
http://example.com/ap1/v1/products/?api_key=1234
I know I can add this when I do App.Products.find({api_key=1234}) but the problem occurs when I do:
var providers = App.Providers.find(1);
providers.get('products');
EDIT:
I have tried to override the buildURL method in the adapter but it's not very convenient since I want to append the api_key param only for certain models.
You should create a second adapter which overrides the buildURL method. Then register that adapter for any types that should be using an api key.
apiAdapter = originalAdapter.extend({
buildURL: ....
}));
Store.registerAdapter(App.Providers, apiAdatper);
See this post for more detail on per-type adapters: How to use DS.Store.registerAdapter