I can't seem to get Ember Data to work with my API format. Right now my API is like so:
{
data: [
{
id: 1,
name: "Some Company",
primary_contact: "Bob Smith"
},
{
id: 2,
name: "Another Company",
primary_contact: "Bob Smith"
},
]
}
I know that Ember wants the key to be organizations rather then data but that is just not possible. I've been trying to get it working with a serializer, and I don't know if I'm even on the right track. Here is what I currently have.
export default DS.RESTSerializer.extend({
normalizeResponse: function(store, primaryModelClass, payload, id, requestType) {
var pluralTypeKey = Ember.String.pluralize(requestType.typeKey);
payload[pluralTypeKey] = payload['data'];
delete payload['data'];
return this._super(store, primaryModelClass, payload, id, requestType);
}
Any help would be GREATLY appreciated!
Use primaryModelClass.modelName instead requestType.
requestType is just string like 'findAll', 'findRecord' and etc.
export default DS.RESTSerializer.extend({
normalizeResponse: function(store, primaryModelClass, payload, id, requestType) {
var pluralTypeKey = Ember.String.pluralize(primaryModelClass.modelName);
payload[pluralTypeKey] = payload['data'];
delete payload['data'];
return this._super(store, primaryModelClass, payload, id, requestType);
}
Working jsbin: http://emberjs.jsbin.com/weyuwixoli/edit?js,output
Related
I know that if I want to serialize nested comments of a model called post, I need to create a serializer in app/serializer/post.js
something like :
import RESTSerializer from 'ember-data/serializers/rest';
import DS from 'ember-data';
export default RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
comments: {embedded: 'always'}
}
});
but what if I want to serialize this inside the app/serlizer/application.js ?
I dont want to define a serializer for each model. Instead I want to be able to resolve the belong-to or has-many relationship inside the normalizeQueryResponse for example.
normalizeQueryResponse(store, primaryModelClass, payload, id, requestType) {
console.log(payload);
return this._normalizeResponse(store, primaryModelClass, payload, id, requestType, true);
},
I want to be able to go thorough the payload and if a property in payload turned out to be object then resolve that.
does anyone know if that is possible?
Sure this is possible and its how serializers and models are meant to work. But I recommend relying on one model and not specifying the type in your case. Lets take this example.
Your model post.js
export default DS.Model.extend((
valueA: DS.attr('string'), // converted to string
valueB: DS.attr('boolean'), // converted to boolean
comments: DS.attr() // not converted and set as it came in the payload
));
Your serializer post.js
export default RESTSerializer.extend({
// assuming your payload comes in the format of
// { data: [ {id: 0, valueA: '', valueB: true, comments: [...]}, {id:1 ...} ]
normalizeQueryResponse(store, primaryModelClass, payload, id, requestType) {
payload.data = payload.data.map((item, index) => ({ // item = {event_type: '', values: ['','']}
id: index, // ONLY if your payload doesnt already have an id
type: 'post', // set the model type
attributes: { // attributes are the values declared in the model
valyeA: item.valueA,
valueB: item.valueB,
comments: item.comments.map( item => {
// remap your comments to whatever format you need
})
}
}));
return payload;
});
Usage in your applicatioin
this.get('store').query('post', {...query object...} ).then(
response => {
let firstItemLoadedComments = response.get('firstObject.values');
// blast comments!
},
error => {
Ember.Logger.error(`cannot load model: ${error}`);
})
.finally(() => {
// set loading to false or something else
});
I'm trying to use embedded records in ember data and I think I'm missing something fundamental.
I have two models
app/models/video.js:
export default DS.Model.extend({
title: DS.attr('string'),
transcriptions: DS.hasMany('transcription', { embedded: 'always' })
});
app/models/transcription.js:
export default DS.Model.extend({
video: DS.belongsTo('video')
});
I also have a custom serializer app/serializers/video.js:
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs:{
transcriptions: { embedded: 'always' }
},
extractSingle: function (store, type, payload, id) {
var data = payload.data;
return {
id: data._id,
title: data.Title,
transcriptions: [{ id: "1" }]
}
}
});
I would expect that this would result in my video model being populated with transcriptions being an array of transcription object but instead I get the following error:
"Error while processing route: videos.show" "Assertion Failed: Ember
Data expected a number or string to represent the record(s) in the
transcriptions relationship instead it found an object. If this is a
polymorphic relationship please specify a type key. If this is an
embedded relationship please include the DS.EmbeddedRecordsMixin and
specify the transcriptions property in your serializer's attrs
object."
Any suggestions of what I'm doing wrong here would be greatly appreciated.
UPDATE: The solution was to modify my custom serializer to the following:
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs:{
transcriptions: { embedded: 'always' }
},
extractSingle: function (store, type, payload, id) {
var data = payload.data;
var videoPayload = {
id: data._id,
title: data.Title,
transcriptions: [{ id: "1" }]
};
return this._super(store, type, videoPayload, id);
}
}
The problem is the fact you're reimplementing extractSingle yourself.
You should call this.super if you're doing this..
In extractSingle on the REST Serializer it calls the normalize function - this normalise function is where the EmbeddedRecordsMixin does all it's work.
Because you're not calling either this.super or manually calling this.normalize you miss out on what the mixin is doing.
I've started using ember data and I'm having some issues getting started. If my json structure for ingredients is:
[
{
"name":"flax seed",
"retailer":"www.retailer.com",
"nutrient_info":[
{
"type":"vitamin A",
"amount":"50mg"
},
{
"type":"calcium",
"amount":"30mg"
}
]
},
{
"name":"soy milk",
"retailer":"www.retailer-two.com",
"nutrient_info":[
{
"type":"vitamin D",
"amount":"500mg"
},
{
"type":"niacin",
"amount":"5000mg"
}
]
},
{ other ingredients... }
]
I think this is how I would define my models:
var attr = DS.attr,
hasMany = DS.hasMany,
belongsTo = DS.belongsTo
App.Ingredients = DS.Model.extend({
// id: attr('number'), // don't include id in model?
name: attr('string'),
retailer: attr('string'),
nutrientinfo: hasMany('nutrients')
})
App.Nutrients = DS.Model.extend({
type: attr('string'),
amount: attr('string'),
ingredient: belongsTo('ingredients')
})
What should the server payload look like, and would I need to customize the REST adapter? Do I need to define the ingredient id: attr() in the model?
Any help in clarifying some of these concepts is appreciated.
Generally model definitions are singular (additionally I changed nutrientinfo to nutrient_info):
App.Ingredient = DS.Model.extend({
// id: attr('number'), // don't include id in model?
name: attr('string'),
retailer: attr('string'),
nutrient_info: hasMany('nutrient')
})
App.Nutrient = DS.Model.extend({
type: attr('string'),
amount: attr('string'),
ingredient: belongsTo('ingredient')
})
The format would need to be as follows (from the endpoint, or using a serializer)
{
// Ingredient records
ingredients:[
{
id:1,
"name":"flax seed",
"retailer":"www.retailer.com",
"nutrient_info":[1,2]
},
{
id:2,
"name":"soy milk",
"retailer":"www.retailer-two.com",
"nutrient_info":[3,4]
},
{ other ingredients... }
],
// Nutrient records
nutrients: [
{
id:1,
"type":"vitamin A",
"amount":"50mg",
ingredient:1
},
{
id:2,
"type":"calcium",
"amount":"30mg",
ingredient:1
},
{
id:3,
"type":"vitamin D",
"amount":"500mg",
ingredient:2
},
{
id:4,
"type":"niacin",
"amount":"5000mg",
ingredient:2
}
]
}
Here's an example using a serializer and your json, I've had to manually assign ids (despite this being invalid, you should send down ids, or use UUIDs), but this should give you an idea of how to use the serializer:
App.IngredientSerializer = DS.RESTSerializer.extend({
extractArray: function(store, type, payload, id, requestType) {
var ingredients = payload,
nutrientId = 0,
ingredientId = 0,
ids = [],
nutrients = [];
ingredients.forEach(function(ing) {
ing.id = ingredientId++;
var nInfo = ing.nutrient_info,
nIds = [];
nInfo.forEach(function(n){
n.id = nutrientId++;
n.ingredient = ing.id;
nIds.push(n.id);
nutrients.push(n);
});
ing.nutrient_info = nIds;
});
payload = {ingredients:ingredients, nutrients:nutrients};
return this._super(store, type, payload, id, requestType);
}
});
http://emberjs.jsbin.com/OxIDiVU/537/edit
Getting this error . Not sure why i am getting this error....would appreciate if someone can help me spot why this is erroring out.
Error while loading route: Error: Assertion Failed: You must include an id in a hash passed to push at new Error (native) at Error.Ember.Error
from the other posts related to similar error this has to do with a json where primary key is not handled correctly. But my json response looks correct.
****here are model objects:****
var PersonInfo = DS.Model.extend({
first: DS.attr('string'),
last : DS.attr('string'),
addresses: DS.hasMany('personAddress', {embedded: 'always'})
});
Ember.Inflector.inflector.irregular("personInfo", "peopleInfo");
export default PersonInfo;
var Address = DS.Model.extend({
type: DS.attr('string'),
personInfo: DS.belongsTo('personInfo')
});
export default Address;
****here is my deserializer:****
var PersonInfoSerializer = DS.ActiveModelSerializer.extend({
primaryKey: 'id',
extractArray: function(store, type, payload, id, requestType) {
var peopleInfo =payload.peopleInfo;
var adds = [];
// debugger;
peopleInfo.forEach(function(personInfo){
var addresses = personInfo.addresses,
addressIds = addresses.mapProperty('id');
adds.push(addresses);
personInfo.addresses = addressIds;
});
payload.addresses = adds;
return this._super(store, type, payload, id, requestType); }
});
export default PersonInfoSerializer;
****and here is the json response which i am mocking in API STUB****
server.get('/peopleInfo', function(req, res) {
var person_info = {
"peopleInfo": [{
"id": "1",
"first": "Tom",
"last": "Dale",
"addresses": [{
"id": "1",
"type": "Home"
}, {
"id": "2",
"type": "Work"
}]
}]
};
res.send(person_info);
});
I'm not sure why you were using the ActiveModelSerializer, but it doesn't really buy you anything if your data isn't coming down in the format that Rails generally provides.
You're data wasn't being formatted correctly. Additionally there is no need to write {embedded:'always'} that does nothing anymore. You'll probably want to look at the transition document https://github.com/emberjs/data/blob/master/TRANSITION.md .
App.PersonInfoSerializer = DS.RESTSerializer.extend({
extractArray: function(store, type, payload, id, requestType) {
var peopleInfo =payload.peopleInfo;
var adds = [];
peopleInfo.forEach(function(personInfo){
//debugger;
var addresses = personInfo.addresses,
addressIds = addresses.getEach('id');
adds = adds.concat(addresses);
personInfo.addresses = addressIds;
});
payload.personAddresses = adds;
return this._super(store, type, payload, id, requestType);
}
});
http://emberjs.jsbin.com/OxIDiVU/477/edit
Edited as I narrowed down issues....
I'm working on an app that is taking in an API - data as JSON. It is an array of groups, with "name", "id", and other such elements.
{
"status": "success",
"data": {
"groups": [
{
"id": 7100,
"name": "Test 12345",
"kind": "floor",
"parent_group_id": 7000,
"controlled_device_type_count": {},
"is_top_level": true
}
]
}}
I also have a livestream websocket - data as JSON stream. It should update the elements referenced in the first API. The two only share "id".
Livestream:
{
"group":{
"usage":{
"10":1,
"20":0,
"30":2,
"40":2
},
"last_change":"2014-03-24T05:56:10Z",
"id":7954
}}
**Updated...**My IndexRoute:
App.ApplicationAdapter = DS.RESTAdapter.extend({
extractArray: function(store, type, payload, id, requestType) {
payload = payload.data;
return this._super(store, type, payload, id, requestType);
}
});
App.IndexRoute = Ember.Route.extend({
sortProperties: ['id'],
sortAscending: true,
beforeModel: function() {
var socket = window.io.connect('http://localhost:8887');
var self = this;
socket.on('group_live_stream', function(data){
var dataObj = JSON.parse(data);
self.store.push('group',dataObj.group);
});
},
actions: {
toggleMenu: function() {
this.controller.toggleProperty('menuVisible');
this.controller.pushBody();
} },
activate: function() {
var self = this;
$.getJSON('http://localhost:3000/api/groups/top?subscribe=true').then(function(data) {
self.store.pushMany('group', data.data.groups);
});
},
model: function() {
return this.store.all('group');
}
});
Updated:
So now I see the livestream coming through - and for a brief, immediate second - I see the API data (group name) and then it disappears. I'm thinking it's because I'm just "pushMany"-ing records and deleting old ones instead of updating. I've heard/read that pushPayload might be my solution....but I can't figure it out. At all (and when I put it in, I just get an error: "Uncaught Error: No model was found for '0' " Help?!
Any thoughts?
Thanks so much!
ptep
Your API's payload is not in the format that ember-data expects (it looks for your payload in the root of your JSON by default). You'll need to override extractArray (and likely extractSingle) on your ApplicationAdapter something like this:
ApplicationAdapter = DS.RESTAdapter.extend({
extractArray: function(store, type, payload, id, requestType) {
payload = payload.data;
return this._super(store, type, payload, id, requestType);
}
});