ngResource transformResponse doesn't seem to be transforming the response - angularjs

I made a base class, HttpService:
class HttpService
constructor: (url) ->
#service = $resource url, {}, #actionOptions()
actionOptions: ->
query:
method: 'GET'
isArray: true
transformResponse: (data) =>
response = []
wrapped = angular.fromJson(data)
angular.forEach wrapped[#indexRoot], (item) =>
response.push #jsonToModel(item)
response
I inherit like this:
class FooService extends HttpService
constructor: ->
super '/api/foos/:id'
#indexRoot = 'foos'
all: ->
#service.query()
jsonToModel: (data) ->
new Foo(data)
In general, the idea is to extend HttpService providing the URL of the resource and the index root (JSON has a root element for the index action). I override jsonToModel to provide a way to transform each JSON element in the response into a custom object, in this case Foo.
When debugging, I can see that response is what it should be in actionOptions(), so it seems like transformResponse is returning what I expect, an array of Foos.
Whenever I call FooService.all() though, I get an array of Resources (ngResource default)... anyone have any idea what I'm doing wrong? I've omitted the dependency injection and everything, but it's all working and I have no errors in console.

The $resource service wraps the response in Resource objects using an interceptor, which occurs after the transformResponse, so while transformResponse is a good place to unwrap a root object (the results from there get fed into the interceptors), you will also need an interceptor to call your jsonToModel function.
The relevant docs:
https://docs.angularjs.org/api/ngResource/service/$resource
transformResponse – {function(data, headersGetter)|Array.} – transform function or an array of such functions. The transform function takes the http response body and headers and returns its transformed (typically deserialized) version. By default, transformResponse will contain one function that checks if the response looks like a JSON string and deserializes it using angular.fromJson. To prevent this behavior, set transformResponse to an empty array: transformResponse: []
interceptor - {Object=} - The interceptor object has two optional methods - response and responseError. Both response and responseError interceptors get called with http response object. See $http interceptors.

Related

Can I create an $http request and not submitting it (yet)?

I have a generic component in my system that deals with submitting HTTP requests in chunks. It takes care of some application-common headers that I need to attach and updating all the necessary places in the GUI.
This component is shared between couple other components for various use-cases. I'm trying to delegate the task of creating HTTP request from a given input to the callers but found out that when the caller does:
var req = $http.get("url", {})
It's already submitting the request.
I'd like the callers to provide a method to generate a list of request objects from the input, and my component will deal with that later (add some headers for example, add success() and error() methods, or submit the requests in batches).
How can I only create the HTTP Request object, without sending it?
You can create wrapper function for "delayed" http request, that would return you preconfigured ready to call function, and use it instead of $http.
Maybe like this:
function dHttp(config) {
return function(options) {
angular.merge(config, options);
return $http(config);
}
}
var req = dHttp({
url: '/some',
data: {test: 21}
});
// later send it
req().then(function(data) {
console.log(data);
});
A plain AJAX can be useful here
xmlhttp.open("GET","a-url",true);
xmlhttp.send();
$http.get() is a declarative form of an ajax open and send.
var reqArr = [];
reqArr.push(xmlhttp.open("GET","a-url",true));
reqArr.push(xmlhttp.open("GET","b-url",true));
reqArr.push(xmlhttp.open("GET","c-url",true));
so you can change the objects as needed and send the ajax later on.

Is it OK to fake a promise on a new angular resource?

I'm working with $resource in Angular 1.3.
I have a bunch of controllers with methods that work with the resource objects.
When the state of retrieval matters, these methods use the $promise property of the resource to ensure that they only process the objects after they are retrieved. These all work fine with existing and updated resource objects.
var ProposalResource = $resource(proposalUrl, {id: '#id'}, {'update': {method: 'PUT'}});
The resource objects are obtained by ProposalResource.get({id:....
However, when I create a new resource object in order to make a new object using new ProposalResource(..., the methods fail because the $promise property is undefined.
I've worked around this by setting the $promise property on the new resource object to a resolved promise.
This seems to work OK but it feels like a nasty kludge. The option of explicitly checking for whether or not the $promise property is defined in all the other methods is even less appealing though.
Am I doing the right thing?
I don't know why you need to use ProposalResource, but I usually use the $q provider.
That way you can do a simple function that returns a promise and you can call it from your controller methods.
Example service that uses a promise:
function someServiceMethod(params) {
//do something here, maybe create an object,maybe make a call with $http or something
var obj = createSomeObject(params);
//this resolves the object once the createSomeObject method or function have completed
$q.when(obj);
}
This approach is easier than doing the whole: var deferred = $q.defer(); and return deferred.promise after the deferred.resolves.
If you're using $resource, then I recommend just using $http provider from angular.
Here's $http
Here's $q
As mentioned in the $resource AngularJS Doucmentation:
Class actions return empty instance (with additional properties
below). Instance actions return promise of the action.
The statement above gives you a hint that instance action methods, $get, $save, ... and any other actions that you define in your $resource action definition, will always return a promise.
DEMO
e.g.
var User = $resource('user.json', {}, {
'update': {'method': 'PUT'}
});
var user = new User();
// this sends a GET request to user.json and returns the promise directly
// from the instance action.
user.$get()
.then(function(latestUserData) {
// latestUserData is also an instance of the User resource
return latestUserData.$update({
'name': 'Ryan'
});
})
.then(function(updatedUserData) {
// do whatever you want here
});
As you can see, the instance action $get returns a promise and when that promise is resolved, the .then() callback returns the response data from the server and at the same time it is wrapped/instantiated with the User $resource.

AngularJS transformResponse

In angularjs resource, I would like to convert my json data into JS objects
//Complex object with inheritance chain
function Car(year, make){
this.make = make;
this.year = year;
}
var carResource = $resource("/api/car/id", {id: '#id'},
{
get: {
method: 'GET',
transformResponse: function(data, headersGetter){
return new Car(data.make, data.year);
}
}
}
)
However this does not seem to be happening
What I am getting back is a $resource object meaning that the properties make and year are set correctly, however the prototype of the returned object points to $resource
Is there a way where I can map my json data directly to my own objects?
Or will I have to write my own 'resource' implementation?
transformResponse is executed on $http level.
When you customise $resource actions with custom config object, that object is actually passed on to the underlying $http service. So if you specify a transformResponse callback, it will be executed on $http level, and the results of your transformation will be passed back on to $resource.
$resource service will instantiate new object from your response data (which is already transformed by the transformResponse callback) and this new object will be an instance of the $resource itself.
So, your car object will be an instance of the Car, but only for a moment, until it's properties are copied into a new $resource object.
Here's a simplistic view of the process:
$resource service initiates request
$http service sends request and receives the response
$http service transforms the response (response is now instance of Car)
$resource service receives the transformed response (from $http)
$resource service makes an instance of itself using transformed response properties (result is now instance of $resource)
Anyway, I don't recommend decorating or extending the $resource service, because it's simpler to write your own implementation using $http service.

Angular $httpProvider transformResponse data contains local HTML DOM elements?

When I instantiate the following code in an AngularJS app, I get weird data in the transformResponse function (bottom of code). I'm not calling the $resource function from any controller, just loading the script in a browser. The data variable (see code) contains the HTML of the current partial, when loading the app.
This seems odd. Is this the way it's supposed to be?
var buddyServices = angular
.module('buddyServices', ['ng','ngResource'])
.factory('Buddy',
function ($resource) { console.log('resource');
return $resource('http://webservice.buddyplatform.com/v1/:service',
{service:'', BuddyApplicationName: 'xxx',
BuddyApplicationPassword: 'yyy'}
);
}
)
.config(function($httpProvider){
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.transformResponse = function(data) {
console.log(data);
return 'TEST: '+data;
};
});
=== EDIT ===
It just daunted on me: $httpProvider handles all http requests, so a page load is one of those. I'm guessing a bit now, but it seems probable. So, the question then becomes: Is there an "easy" way to constrain the data in the code above to only those requests performed by my service?
transformResponse takes another parameter headersGetter. You can use this to get the headers send with the response. Look for Content-Type header header. It should contain application/json

angularjs $resource callback

I have this model called Project and whose members are rows in a table, more specifically, a table decorated by the jQuery DataTables plugin
Here's my project model in coffeescript
#= require vendor/angular.js
Project = angular.module('Project', ['ngResource'])
.value( 'csrf', $('meta[name="csrf-token"]').attr('content') )
.factory('Project', ['$resource','csrf', ($resource, csrf) ->
$resource '/projects/:project_id/:action', {authenticity_token: csrf, project_id:'#id'},
query:
method: 'GET'
isArray: yes
new:
method: 'GET'
params:
project_id: 'new'
edit:
method: 'GET'
update:
method: 'PUT'
])
Ene drawback of the DataTable plugin used alongside angular is that whenever I so a project.$update(), that project disappears from the view
So I have to sort of reload the DataTable by re-initializing it, I can live with that for now but I was wondering then, if there is a way that I can set a universal callback for $resource, where I can put this DataTables re-initialization call?
thanks!
I'm assuming that in the controller from whence you call the factory you have some sort of wrapper method.
lets call it scope.crudData
SomeCtrl = (scope, http, projectService)
scope.crudData(p_id, act, method) ->
meth = YOUR LOGIC FOR PARSING AN ACTUAL FUNCTION FROM THE METHOD NAME GOES HERE
projectService.meth
project_id: p_id
act: action
, (response) ->
THIS IS WHERE YOUR CALLBACK LIVES. KNOCK YOURSELF OUT. RELOAD THE DATA DO ANYTHING YOU WANT
If that's not good enough, you could I suppose register a listener in config for all ajax requests, verify the data, etc.
Let me know if that's what you are looking for and I'll put together some pseudocode for you.

Resources