AngularJS 1.2.1
ngResource 1.2.1
I got the weirdest problem.
I'm using tokenWrapper by Andy Joslin (AngularJS: How to send auth token with $resource requests?)
I have a resource defined like that:
.factory('someService', ['$resource', 'api_host', 'TokenHandler',
function($resource, api_host, TokenHandler) {
var Resource = $resource(api_host + 'applicant/:command/:xxx', { xxx: '#xxx', command: '#command' }, {
'get': { method: 'GET', isArray: false },
'save': { method: 'POST', isArray: false },
'create': { method: 'put', isArray: false },
'message': { method: 'post', isArray: false }
});
Resource = TokenHandler.wrapActions( Resource,
["query", "get", "save", "remove", "create", "message"] );
return Resource;
}])
its wrapped by tokenHandler and token is sent with every request and thats great.
the problem is with invoking the error callback.
when using the resource like that
var interview = new someService({ command: 'doSomething', xxx: $scope.xxx});
interview.$create({}, function(response) {
console.log(response);
}, function(e) {
alert('error');
});
The Problem
When the resource returns 200 the success function (1st argument) is invoked as it should.
When the resource returns something else, the error callback is not invoked.
I've tried to log the tokenHandler and it seems like the handler is massing up the arguments.
var tokenWrapper = function( resource, action ) {
// copy original action
resource['_' + action] = resource[action];
// create new action wrapping the original and sending token
resource[action] = function( data, success, error){
console.log(success, 'success');
console.log(error, 'error');
return resource['_' + action](
angular.extend({}, data || {}, {token: tokenHandler.get()}),
success,
error
);
};
};
the result of the console.log prints out the data as success and success as error.
how can I fix that?!
angular 1.2.0 changed the way promises are handled. and this look like a promise issue (you are not getting the expected order of suceess``failure```. see this for an example. my guess that insisting on using resource from 1.5 doesn't work we'll with this behavior, so you are not getting the expected data. yes. there are error messages when upgrading. but solving them shouldn't take long.
Related
I am currently using angular-hateoas (https://github.com/jmarquis/angular-hateoas). I would like to add specific interceptors to the query() and get() functions of the resource created in HateoasInterface. I have been looking for ways to do it, but not been successful.
I thought it could be done by adding it like this:
var someResource = someService.resource('someresource');
someResource.query.interceptors = {
response: function (data) {
// do something data
return data
},
responseError: function (error) {
// do something with error
return $q.reject(error);
}
};
but that gives me:
TypeError: Attempted to assign to readonly property.
I might need to use $decorator, but I have no experience with that, and I have seen no example for adding specific interceptors to specific resource objects.
I don't really want to use $httpProvider.interceptors, since I don't want the interceptor to work on all resources.
The only thing I can currently think of, is configuring HateoasInterfaceProvider with specificly named functions that contain the specific interceptors.
angular.module('myModule')
.config(HateoasInterfaceConfig);
HateoasInterfaceConfig.$inject = ['HateoasInterfaceProvider'];
function HateoasInterfaceConfig(HateoasInterfaceProvider) {
HateoasInterfaceProvider.setHttpMethods({
get: {
method: 'GET',
isArray: false
},
getSomeResource: {
method: 'GET',
isArray: false,
interceptors: {
response: someResponseFunc,
responseError: someErrorFunc
}
},
update: {
method: 'POST',
},
query: {
method: 'GET',
isArray: true
}
querySomeResource: {
method: 'GET',
isArray: true,
interceptors: {
response: function(data) {
// do something with data
return data;
},
responseError: function (error) {
//do something with error
return $q.reject(error);
}
}
});
HateoasInterfaceProvider.setLinksKey('_links');
}
but I prefer not to do it like that.
Figured it out.
When calling a resource, params and actions can be passed.
So like:
someServiceResult.resource('someresource',{},{get: {method: 'GET',...,
interceptor: { response: responseInterceptorFunc, ...}}})
Still not really the preferred solution, but when wrapped in a function in a service, acceptible.
I would like to have a solution that allows changing the interceptor definition for the Resource object created with:
someServiceResult.resource('someresource')
but I currently don't have time for figuring that out.
In typescript I have a DataAccess Class so that all Ajax calls are routed through a single object to save repetition of code in a lot of places within my application.
In using this approach I have needed to use call backs to get the response back to the calling class so that the success and error can be handled accordingly.
This is the typescript
ajaxCall(retVal, retError) {
$.ajax({
type: this.callType,
data: this.dataObject,
dataType: this.dataType,
url: this.url,
contentType: this.contentType,
traditional: this.traditional,
async: this._async,
error: retError,
success: retVal
});
}
This is the compiled Javascript
AjaxDataAccessLayer.prototype.ajaxCall = function (retVal, retError) {
$.ajax({
type: this.callType,
data: this.dataObject,
dataType: this.dataType,
url: this.url,
contentType: this.contentType,
traditional: this.traditional,
async: this._async,
error: retError,
success: retVal
});
};
return AjaxDataAccessLayer;
This calls through to the ASP.Net MVC controllers perfectly fine, however the problem that I have is regardless of Success or Error the call back is always retError.
This is the calling Typescript
var _this = this;
var dataAccess = new DataAccess.AjaxDataAccessLayer(Fe.Upsm.Enums.AjaxCallType.Post,
Fe.Upsm.Enums.AjaxDataType.json,
"../../PrintQueue/DeletePrintQueueItems",
jsonObj);
dataAccess.ajaxCall(data => {
// success
new Fe.Upsm.Head().showGlobalNotification("Selected Items Deleted");
_this.refreshPrintQueueGrid();
(window as any).parent.refreshOperatorPrintQueueCount();
}, xhr => {
// failure
alert("An Error Occurred. Failed to update Note");
});
When stepping through and looking at this the Status is OK and the response is 200.
So, Problem (as mentioned above) always calling xhr \ retError regardless of success.
Question: How do I get it to go into the right call back?
In your error handler, you were not passing all the parameters, so you are only checking whether the request finished successfully. However, there can be errors after that, like when the response is processed. You can handle errors betters like this:
dataAccess.ajaxCall(data => {
// success
new Fe.Upsm.Head().showGlobalNotification("Selected Items Deleted");
_this.refreshPrintQueueGrid();
(window as any).parent.refreshOperatorPrintQueueCount();
}, (xhr, errorText, errorThrown => {
// failure
console.log(xhr, errorTest, errorThrown);
alert("An Error Occurred. Failed to update Note");
});
Based on the discoveries using this method, the error is that your controllers are returning empty responses, so you're getting an exception when jQuery tries to parse them, because an empty string is not valid JSON.
Note: I have limited exp with js so correct me if my I'm completely wrong in how I'm describing this scenario.
I have two javascript files. I am calling a function on the first file (client side) which calls a function on the second file and uses the callback from the second file's function for the purposes of response.success/.error on the first file.
If that doesn't make sense here is some code:
Note: this is being done temporarily using Parse's cloud functions. Let me know if more information is needed regarding those but not sure if it's important.
First file:
Parse.Cloud.define("methodName", function(request, response) {
...
secondFile.myFunction(param1, {
stuff: request.params.stuff,
}, function (err, res) {
if (err) {
response.error(err);
} else {
response.success(res);// I'm assuming this is going to the hardcoded "yes." from httpRequest on second file's function
}
});
});
Second File:
myFunction: function(param1, properties, callback) {
if (!param1) return callback(new Error("Helpful error message"));
var headersForReq = {
...
};
var bodyForReq = ...; // the properties properly parsed
Parse.Cloud.httpRequest({
method: 'PUT',
url: ...,
headers: headersForReq,
body: bodyForReq,
success: function (httpResponse) {
callback(null, 'yes'); // the hardcoded "yes" i referred to
},
error: function (httpResponse) {
callback(httpResponse.status + httpResponse.error);
}
});
}
On my the client, the code is treated as a success (errors aren't thrown or returned back) but when I print out the value it comes across as (null) not "yes".
What's going on here? (Side note, httpRequest is currently not doing anything, its hard to verify if the request is properly being sent because it's being sent to a third party API).
I do know the second file's method is properly being called though. So it's not a silly issue with the module.exports or var secondFile = require('\path\secondFile')
I think you are just mis-use the api
Rewrite it with the example style.
https://parse.com/docs/js/api/classes/Parse.Cloud.html#methods_httpRequest
Parse.Cloud.httpRequest({
method: 'PUT',
url: ...,
headers: headersForReq,
body: bodyForReq
}).then(function (httpResponse) {
callback(null, 'yes'); // the hardcoded "yes" i referred to
},
function (httpResponse) {
callback(httpResponse.status + httpResponse.error);
}
});
I think below will work, too.
Parse.Cloud.httpRequest({
method: 'PUT',
url: ...,
headers: headersForReq,
body: bodyForReq
}, {
success: function (httpResponse) {
callback(null, 'yes'); // the hardcoded "yes" i referred to
},
error: function (httpResponse) {
callback(httpResponse.status + httpResponse.error);
}
});
BTW, if you are using open source parse-server, you can use request or request-promise. These 2 npm package is used by many people. (Parse.Promise is not es6-like promise)
I'm using angularjs-1.5.0 to write my application.
I created a service that uses a $resource to fetch data from the api server. the service is called MyApi.
(function () {
angular.module('myalcoholist').factory('MyApi', ['$resource', '$auth', function ($resource, $auth) {
return $resource('https://myalcoholist.com:8888/drink/:cmd',
null, {
pouringSpeedTypes: {
method: 'GET',
params: {api_token: $auth.getToken(), cmd: 'get_pouring_speed_types'},
isArray: true
},
pouringLocationTypes: {
method: 'GET',
params: {api_token: $auth.getToken(), cmd: 'get_pouring_location_types'},
isArray: true
}
});
});
}]);
})();
and in my controller i use this service using the following code:
MyApi.pouringLocationTypes().$promise.then(function(data) {
console.info(data);
});
MyApi.pouringSpeedTypes().$promise.then(function (data) {
console.info(data);
});
the problem is that the data is.. a promise i guess.
this is what data contains:
$promise: d
$resolved: true
length: 0
__proto__: Array[0]
what do I do with that? how do I get the actual data from the get request ?
thank you for your comments, you allowed me to pinpoint the issue.
first of all I didn't update my server's code that's why when you tried these addresses you got Method not Allowed.
the problem that i've been having is that these requests really returned empty arrays.
so first of all my API returns a json Object not an array so i changed isArray to false.
so after that and actually fixing my API Server, this is what I received:
$promise: d
$resolved: true
data: Array[4]
success: true
my API server returns a json with data and success. so things are working now! thanks a lot.
I have the following service defined to handle my User resource. The server returns namespaced JSON data, and expects namespaced JSON data in return. Unfortunately, while I've been able to to configure it to read the data, it refuses to namespace the output data. The closest I've come is with the writeUserKey function, and that just causes the server to complain. I think it's overriding the JSON encoding, causing angular to emit just [object object] instead of a JSON string. How do I get this to work?
Code:
servicesModule.factory('User', ['$resource', '$q', 'API_BASE',
function($resource, $q, apiPath){
var readUsersKey = function(data, headersGetter){
if(data.users) return data.users;
return data;
};
var writeUserKey = function(data, headersGetter){
data = { user: data};
return data;
};
var readUserKey = function(data, headersGetter){
if (data.user) return data.user;
return data;
};
return $resource(apiPath+'users/:id', {id:'#_id'},{
index: { method: 'get', isArray:true, responseType: 'json', transformResponse:readUsersKey},
show: { method: 'get', responseType: 'json', transformResponse:readUserKey},
update: { method: 'put', responseType: 'json', transformResponse:readUserKey,
transformRequest:writeUserKey},
create: { method: 'post', responseType: 'json', transformResponse:readUserKey,
transformRequest:writeUserKey}
});
}]);
Original output: { "key":"value","key2":"value2","key2":"value2" }
Current Output: [object object]
Desired Output: { "user": { "key":"value","key2":"value2","key2":"value2" } }
Sometimes lunch helps. After grabbing some food and puttering around on other parts of hte project, I realized the solution was insanely obvious. If I'm overriding the default serialization, I just need to reproduce a simple .toJson() call.
Change writeUserKey to:
var writeUserKey = function(data, headersGetter){
return angular.toJson({ user: data});
};
Still seems like this is way more complex than something that should be easily done out-of-the-box should require, but it works. If there's a better approach to handling namespaced data, please let me know!