Customising RESTAdapter in ember - json

I have an ember application which has two routes, user and subject. I'm (trying) to use the RESTAdapter to get data from an api, but the api does not return the data in the necessary format, ie instead of returning:
{
"user": {
id: "1",
name: "John Doe"
}
}
my api returns:
{
id: "1",
name: "John Doe"
}
I've been reading through a lot of related questions and have surmised that I need to use some kind of serializer within my adapter to customize it for my JSON. So far I have come up with this, based on answers and examples:
App.Adapter = DS.RESTAdapter.extend({
host: 'http://example.com',
serializer: DS.RESTSerializer.extend({
extractMany: function(loader, json, type, records) {
var root = this.rootForType(type);
var roots = this.pluralize(root);
formattedJson = {};
formattedJson[roots] = json.entries;
delete formattedJson.pagination;
this._super(loader, formattedJson, type, records);
}
})
});
But it doesn't seem to be having any effect. I'm also wondering if I need to write a different adapter for each model as I don't think the above can handle creating both users and a subjects roots. Can anyone point me in the right direction?

Per type serializers are super easy in ED as of 1.0, see documentation below, and this example should work for your user model.
https://github.com/emberjs/data/blob/master/TRANSITION.md
App.UserSerializer = DS.RESTSerializer.extend({
extractSingle: function(store, type, payload, id, requestType) {
payload = {user:payload};
return this._super(store, type, payload, id, requestType);
}
}

Related

EmberJS 2.7 How to restructure/reformat/customize data returned from the store

I have what I think is a very simple issue, but I just don't get how to do this data manipulation. This sadly didn't help, even though it's the same pain I am feeling with Ember.
Here is a route:
route/dashboard.js:
import Ember from 'ember';
export default Ember.Route.extend({
// this is for testing, normally we get the data from the store
model: function() {
return this.get('modelTestData');
},
modelTestData: [{
name: 'gear',
colorByPoint: true,
data: [
{y: 10, name: 'Test1'},
{y: 12, name: 'Test2'},
{y: 40, name: 'Test3'}
]
}],
});
The structure of the 'modelTestData' object has to be exactly like that as it is passed into a child component that needs it structured that way.
I can easily get my data from the API and put it into the model:
model: function() {
return this.store.get('category');
},
But then I need to restructure it...but how?
I have to somehow iterate over the categories collection and extract parts of data from each record to replace the 'data' part of the modelTestData object.
So I have 3 issues I am completely stumped on:
How to 'get at' the attributes I need from the model?
How to structure them as an array of objects with 'y' and 'name'?
How to assign that structure to the 'data' property of modelTestData instead of it being hardcoded?
Categories is a JSONAPI object like this:
{
"data":[
{
"id":"1",
"type":"categories",
"attributes":{
"name":"Carrying system",
"total-grams":"0.0"
}
},
{
"id":"2",
"type":"categories",
"attributes":{
"name":"Shelter system",
"total-grams":"0.0"
}
}
]
}
I need to map the grams value to 'y' and the name to 'name' in modelTestData.
Note that the category data is used in other routes for other purposes exactly as returned by the API. So I don't want to change the model structure itself, or what the API returns...that will break other parts of the app that do use 'category' in its original structure.
This is a specific use case that this route needs to massage the data to pass to the child component as per the structure of modelTestData.
I also wonder whether this data manipulation task belongs in a route?
Should I somehow do this in the serliazer adapter, creating a new structure as say 'categoryWeights' so I can then do:
model: function() {
return this.store.get('categoryWeights');
},
EDIT
I have managed to do this in the route, but it just gives me an array of objects. I need a single object containing 2 properties and an embedded array of objects.
model() {
return this.store.findAll('category')
.then(categories => categories.map(category => {
let data = {
y: category.get('totalGrams'),
name: category.get('name')
};
return data;
}))
},
This should probably go into a computed property:
dataForSubModel: Ember.computed('model.#each.totalGrams', 'model.#each.name', {
get() {
return [{name: 'gear', colorByPoint: true, this.get('model').map(m => ({y:m.get('totalGrams'), name:m.get('name')}))}
}
}),
The serializer is the wrong place, because its not that you need to convert it between the server and your app, but between your app and a strange component.
Actually the best thing would be to refactor the component.
Ok I got this to work in the route.
model() {
return this.store.findAll('category')
.then( function(categories) {
let data = [];
data = categories.map(category => {
return {
y: category.get('totalGrams'),
name: category.get('name')
}
});
return [{name: 'gear', colorByPoint: true, data}];
})
},
I still have the feeling this should be done in the adapter or serializer. Would be happy to see answers that show how to do that.

How can I serialize a Simple array payload in ember?

I am pretty new to Ember. I have a service which returns a simple array like
[
"abc",
"bcd",
"cde",
"def",
"efg"
]
My model is somewhat like this
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
value: attr()
});
In the serializer(I am trying with RESTSerializer), I want this data to be sent back to route.js where the service call is made. The service call is to an API which I am not allowed to change in any way.
I tried a lot of ways that might be stupid and googled a lot. Sadly I could not find a solution though I believe it may not be too tough.
I got the payload in serializer as pasted above and was able to log the response. From there what is to be returned and what serializer is apt is my current problem. Kindly ask me if any further details are required to figure this out. I am not posting much so that I can keep it simple and understandable. Any help is appreciated.
You may not want to use Ember Data. However, you can by implementing normalizeResponse in your Serializer.
For example, if your model name is "account":
export default DS.RESTSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
let newPayload= {
accounts: [{
value: payload
}]
};
return this._super(store, primaryModelClass, newPayload, id, requestType);
}
});

What's the best way to map objects into ember model from REST Web API?

The topic of this post is: my solution is too slow for a large query return.
I have a Web Api serving REST results like below from a call to localhost:9090/api/invetories?id=1:
[
{
"inventory_id": "1",
"film_id": "1",
"store_id": "1",
"last_update": "2/15/2006 5:09:17 AM"
},
{
"inventory_id": "2",
"film_id": "1",
"store_id": "1",
"last_update": "2/15/2006 5:09:17 AM"
}
]
Since my WebAPI did not provide a root key for my JSON response, I made a RESTSerializer like following.
export default DS.RESTSerializer.extend({
extract:function(store,primaryType,payload,id,requestType){
var typeName = primaryType.typeKey;
var data = {};
data[typeName] = payload; // creating root
payload = data;
return this._super(store,primaryType,payload,id,requestType)
}
});
When this gets run, I get the following error message: Assetion failed: You must include an 'id' for inventory in an object passed to 'push'
As you can see, these objects do not have the attribute id, so I found that the default behaviour of Ember RESTSerializer forces me to write my own.
Okay, so here's where I'm not sure my solution is right. inventory_id from my return is unique, therefore I choose to use that as an id, okay I'm thinking to my self, I'll just add it manually. The function looks like this now:
export default DS.RESTSerializer.extend({
extract:function(store,primaryType,payload,id,requestType){
var typeName = primaryType.typeKey;
for(var i=0;i<payload.length;i++){
payload[i].id = payload[i].inventoryId;
}
var data = {};
data[typeName] = payload; // creating root
payload = data;
return this._super(store,primaryType,payload,id,requestType)
}
});
By just manually duplicating an attribute, I feel like I'm cheating my way over this error message. In addition, I sometimes return a large payload array (over 150k rows). Looping O(n) just doesn't seem a right price to pay for just a simple mapping.
Is there some other way to set either my WebAPI or serializer up so I avoid the for loop in assigning the id that ember so desperately wants.
I think this should fix your problem:
export default DS.RESTSerializer.extend({
primaryKey: 'inventory_id'
});
With this parameter Ember Data will map inventory_id to it's id parameter.

Ember Data 1.0 with none standard json

I'm looking to use json from Github's issues api with Ember Data.
The problem is that the return json is not formatted the way ember data wants it to be.
Github returns data as such.
[
{id: 0, title: 'test'},
{id: 1, title: "ect.."}
]
Where as Ember Data's RestAdapter expects it to be like:
{ issues: [
{id: 0, title: 'test'},
{id: 1, title: "ect.."}
] }
I assume based on some research that I need to override the Find and FindAll methods for the DS.RestAdapter
MyApp.Issue = DS.Model.extend({ ... });
MyApp.IssueAdapter = DS.RESTAdapter.extend({
host: 'https://api.github.com/repos/emberjs/ember.js',
find: function(store, type, id) {
...
},
findAll: function(store, type since) {
...
},
}
The problem is in the example if found there are a few out of data methods and it also does not explain how I can pass a array with out an key of "issues"
To be honest I'm really a complete noob to all this and most of this js is over my head but I feel like I'm close to figuring this out. Let me know if I'm on the right track.

Sending complex JSON with fetch, save, and delete on a model or collection

We have an internal API that was specifically built to be used with a new piece of software I'm building that runs on Backbone. The API has a single URL and takes JSON as input to determine what it needs to return. It essentially allows me to build custom queries with JSON that return exactly what I'm looking for.
Thing is this JSON can get pretty verbose and is often 3–4 levels deep, but sometimes may just be a few lines and just 1 level deep.
First question first: How do I send a string of JSON along with the ID when I do a fetch()? Do I have to set these parameters as the model or collection's defaults?
Here is an example of a really simple string to get a specific user's info
{
"which" : "object",
"object" : {
"type" : "customer",
"place" : "store",
"customerID" : "14"
}
}
As others have suggested it will likely be challenging to work with SOAP, but it shouldn't be impossible. Backbone models and collections communicate with the server through the sync operation; you should be able to customize that. I think something along these lines might get the ball rolling (for models):
Backbone.SoapyModel = Backbone.Model.extend({
sync: function(method, model, options) {
// force POST for all SOAP calls
method = 'create';
options = _.extend(options, {
// Setting the data property will send the model's state
// to the server. Add whatever complexity is needed here:
data: JSON.stringify({
"which" : "object",
"object" : model.toJSON()
}),
// Set the request's content type
contentType: 'application/json'
});
// Defer the rest to Backbone
return Backbone.sync.apply(this, [method, model, options]);
}
});
var SoapyModelImpl = Backbone.SoapyModel.extend({
url: '/test'
});
var soapTest = new SoapyModelImpl({
id: 42,
name: 'bob',
address: '12345 W Street Dr',
phone: '867 5304'
});
soapTest.fetch();