Ember data 1.0 Application -en AdapterSerializer not working? - serialization

My question is in line with this one: Ember data 1.0.0: confused with per-type adapters and serializers
Problem is that I cannot initialize RESTSerializer per type because I need to set ActiveModelSerializer :
App.FooSerializer = DS.ActiveModelSerializer.extend({attrs: {}});
to set relationships as embedded.
So I want to set 1 serializer for all models. I tried to set ApplicationSerializer, but this did not call any hooks when getting response from server (And I'm sure my server gives correct response):
App.ApplicationSerializer = DS.RESTSerializer.extend({
extractSingle: function(store, type, payload, id, requestType) {
return this._super(store, type, payload, id, requestType);
},
extractArray: function(store, type, payload, id, requestType) {
return this._super(store, type, payload, id, requestType);
},
normalize: function(type, property, hash) {
return this._super(type, property, hash);
}
});
Setting my adapter doesn't seem to work:
var adapter = require('init/adapter');
App.ApplicationAdapter = adapter.extend({
defaultSerializer: myAdapter //I QUESS THIS IS WRONG?
});
Did I make a syntax error? Any other suggestions?
EDIT:
Ok, I found my mistake but not a great solution. Seems that my FooSerializer overrides the general ApplicationSerializer..
Is there a way where I can set both? :/

You can't use both per say, but you can have your FooSerializer extend your ApplicationSerializer and then override the methods that you'd like to use differently.
App.FooSerializer = App.ApplicationSerializer.extend({
extractArray: function(store, type, payload, id, requestType) {
// alert each record to annoy the user
return this._super(store, type, payload, id, requestType);
},
});

Related

Create - How to handle empty response

My API doesn't return any response in body on POST,PATCH, etc for some resources.
How are we supposed to handle this case in data provider ?
I tried to return an empty data object but unfortunately I get the error :
"The response to 'CREATE' must be like { data: ... }, but the received response does not have a 'data' key. The dataProvider is probably wrong for 'CREATE'."
I don't know how your dataProvider looks like or what the response of your API for POST/PUT requests is, but the problem is probably located in your dataProvider's convertHTTPResponseToREST as this is responsible for handling your API's responses.
Try the following:
case CREATE:
return { data: { ...params.data, id: json.id } };
If this doesn't work either, then try this one:
case CREATE:
return { data: json };

How to call web api Controller method with two parameters using angularjs

I am pretty new in angularjs and I've looked around to try to find some posts on this and there are many but none that address my specific question (that I could find).
It is as simple as that, I want to send two parameters through angularjs ($http POST) where my first parameter is a json of class object and second is int. What I tried :
var url = '../Request/'+ id;
$http({
method: 'POST',
url: url,
data: Data
}).success(function (data, status, headers, config) {
deferred.resolve(data);
}).error(function (data, status, headers, config) {
debug.error(data);
deferred.reject('An error occured while saving the request');
});
In my web api Controller I have :
[POST("Request/{id}")]
public bool SaveRequest(Data data, int id)
{
...
...
}
When I send only Data it works for me but when I tried to add Id and Data both it won't work. Please let me know what needs to be done for the same, Thanks.
Have you tried using [FromBody] attribute like this
[POST("Request/{id}")]
public bool SaveRequest([FromBody] Data data,[FromUrl] int id)
{
...
More info on parameter binding

Get model with both id and query parameters

I have a route that should load a model (BatchDetail) and a number of related items (BatchItems). Since there are a great number of items I should be able to do pagination with the help of two request parameters, limit and offset.
Here is the route I set up:
App.BatchDetailRoute = Ember.Route.extend({
model: function(params) {
var store = this.get('store');
var adapter = store.get('adapter');
var id = params.batch_detail_id;
var rejectionHandler = function(reason) {
Ember.Logger.error(reason, reason.message);
throw reason
}
return adapter.ajax("/batch_details/" + id, "GET", {
data: { limit: 50, offset: 100 }
}).then(function(json) {
adapter.didFindRecord(store, App.BatchDetail, json, id);
}).then(null, rejectionHandler);
},
setupController: function(controller, model) {
return this.controllerFor('batchItems').set('model', model.get('items'));
}
})
This way, when I go to /batch_details/1 my REST adapter will fetch the correct data which I receive in json in the above code.
Now, the model hook should return a model object or a promise that can be resolved to a model object, and that's where the problem lies. In setupController (which runs after the model hook) model is set to undefined and so my code explodes.
That means that whatever adapter.ajax returns does not resolve correctly but instead returns undefined. I'm baffled, since the above mechanism is exactly how the different find methods in ember-data (findById, findByQuery, etc.) work and that's where I got my idea from.
Can you shed some light on what I'm not getting?
Thank you.

Dojo datagrid jsonrest response headers

I'd like to use custom headers to provide some more information about the response data. Is it possible to get the headers in a response from a dojo datagrid hooked up to a jsonRest object via an object store (dojo 1.7)? I see this is possible when you are making the XHR request, but in this case it is being made by the grid.
The API provides an event for a response error which returns the response object:
on(this.grid, 'FetchError', function (response, req) {
var header = response.xhr.getAllResponseHeaders();
});
using this I am successfully able to access my custom response headers. However, there doesn't appear to be a way to get the response object when the request is successful. I have been using the undocumented private event _onFetchComplete with aspect after, however, this does not allow access to the response object, just the response values
aspect.after(this.grid, '_onFetchComplete', function (response, request)
{
///unable to get headers, response is the returned values
}, true);
Edit:
I managed to get something working, but I suspect it is very over engineered and someone with a better understanding could come up with a simpler solution. I ended up adding aspect around to allow me to get hold of the deferred object in the rest store which is returned to the object store. Here I added a new function to the deffered to return the headers. I then hooked in to the onFetch of the object store using dojo hitch (because I needed the results in the current scope). It seems messy to me
aspect.around(restStore, "query", function (original) {
return function (method, args) {
var def = original.call(this, method, args);
def.headers = deferred1.then(function () {
var hd = def.ioArgs.xhr.getResponseHeader("myHeader");
return hd;
});
return def;
};
});
aspect.after(objectStore, 'onFetch', lang.hitch(this, function (response) {
response.headers.then(lang.hitch(this, function (evt) {
var headerResult = evt;
}));
}), true);
Is there a better way?
I solved this today after reading this post, thought I'd feed back.
dojo/store/JsonRest solves it also but my code ended up slightly different.
var MyStore = declare(JsonRest, {
query: function () {
var results = this.inherited(arguments);
console.log('Results: ', results);
results.response.then(function (res) {
var myheader = res.xhr.getResponseHeader('My-Header');
doSomethingWith(myheader);
});
return results;
}
});
So you override the normal query() function, let it execute and return its promise, and attach your own listener to its 'response' member resolving, in which you can access the xhr object that has the headers. This ought to let you interpret the JsonRest result while fitting nicely into the chain of the query() all invokers.
One word of warning, this code is modified for posting here, and actually inherited from another intermediary class that also overrode query(), but the basics here are pretty sound.
If what you want is to get info from the server, also a custom key-value in the cookie can be a solution, that was my case, first I was looking for a custom response header but I couldn't make it work so I did the cookie way getting the info after the grid data is fetched:
dojo.connect(grid, "_onFetchComplete", function (){
doSomethingWith(dojo.cookie("My-Key"));
});
This is useful for example to present a SUM(field) for all rows in a paginated datagrid, and not only those included in the current page. In the server you can fetch the COUNT and the SUM, the COUNT will be sent in the Content-Range header and the SUM can be sent in the cookie.

ExtJS: Model.save() error "cannot read property 'data' of undefined" when server response is simple success

(ExtJS 4.0.7)
I'm using Model.save() to PUT an update to a server. Everything works fine and the server returns a simple JSON response {success: true} (HTTP status 200). Model.save() throws the following error, however:
Uncaught TypeError: Cannot read property 'data' of undefined
Here's where this is happening in the ExtJS code (src/data/Model.js):
save: function(options) {
...
callback = function(operation) {
if (operation.wasSuccessful()) {
record = operation.getRecords()[0]; <-- getRecords() return an empty array
me.set(record.data); <-- record is undefined, so .data causes error
...
}
I've figured out this is happening because Model.save() expects the server to respond with JSON for the entire object that was just updated (or created).
Does anyone know of a clever way to make Model.save() work when the server responds with a simple success message?
I was able to come up with a work-around by using a custom proxy for the model, and overriding the update function:
Ext.define('kpc.util.CustomRestProxy', {
extend: 'Ext.data.proxy.Rest',
alias: 'proxy.kpc.util.CustomRestProxy',
type: 'rest',
reader : {
root: 'data',
type: 'json',
messageProperty: 'message'
},
// Model.save() will call this function, passing in its own callback
update: function(operation, callback, scope) {
// Wrap the callback from Model.save() with our own logic
var mycallback = function(oper) {
// Delete the resultSet from the operation before letting
// Model.save's callback use it; this will
oper.resultSet = undefined;
callback(op);
};
return this.doRequest(operation, mycallback, scope);
}
});
In a nutshell, when my proxy is asked to do an update it makes sure operation.resultSet == undefined. This changes the return value for operation.getRecords() (which you can see in the code sample from my question). Here's what that function looks like (src/data/Operation.js):
getRecords: function() {
var resultSet = this.getResultSet();
return (resultSet === undefined ? this.records : resultSet.records);
}
By ensuring that resultSet == undefined, operation.getRecords returns the model's current data instead of the empty result set (since the server isn't returning a result, only a simple success message). So when the callback defined in save() runs, the model sets its data to its current data.
I investigate this problem and found truly simple answer. Your result must be like this:
{
success: true,
items: { id: '', otherOpt: '' }
}
And items property MUST be equal Model->Reader->root property (children in tree for example).
If you want to use items instead children you can use defaultRootProperty property in Store and configure your nested collections as you want.
PS
Object in items property must be fully defined because it replaces actual record in store.