Angular $resource JSON Callback not working - is this best practice? - javascript

I'm creating a resource to pass data into my controller for an existing api that need to hook into. I am not able to modify the back end unfortunately.
My Resource factory currently looks like this:
'use strict';
angular.module('XXX')
.factory('elements', function (
$resource
) {
return $resource('http://XXX/api/v1/elements/:id',
{
callback: 'JSON_CALLBACK',
id: '#id'
},
{
query: {
method: 'JSONP',
params: {
id: '#id'
}
},
all: {
method: 'JSONP',
params: {}
}
}
);
});
The elements.query() works fine, however the elements.all() does not work unfortunately. I did notice that in the returned content in my network tab, begins with angular.callbacks._2([{... DATA...}]) - this doesn't seem right to me.
UPDATE.....
OK so i've got it working with this:
angular.module('XXX')
.factory('element', function (
$resource
) {
return $resource('http://XXX/api/v1/elements/:id',
{
id: '#id',
callback : 'JSON_CALLBACK',
},
{
query: {
method: 'JSONP',
params: {
id: '#id'
}
},
all: {
method: 'JSONP',
isArray: true,
params: {
callback : 'JSON_CALLBACK',
}
}
}
);
});
however the json that it returns to the console comes in as an array.. I am able to parse it for use, but just wondering now if this is best practice?

That is the way isArray is meant for.
You need the isArray flag, because angular has to create an empty Object - in this case an Array - before the request is sent. So all bindings will work, because you have the same reference. At the time the result has arrived, it will be populated to your variable, the result object / array.
We are doing the service calls like this, maybe it will help you:
.factory('Person', function($resource, AppConfig) {
var Person = $resource(AppConfig.remoteURL + '/person/:id', {}, {
query: {method:'GET'},
queryAll: {method:'GET', isArray: true},
create: {method:'POST'},
update: {method: 'PUT'}});
return Person;
});
and call it like this:
Person.queryAll(function (result) {
angular.isArray(result); // should be true
}, function (error)
{
//doh!
}

You might want to specify a custom transformResponse to convert pull the data out of the array form.
Snippet from $resource docs:
transformResponse – {function(data, headersGetter)|Array.<function(data, headersGetter)>} – 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.
So your code would be something along the lines of:
all: {
method: 'JSONP',
isArray: true,
params: {
callback : 'JSON_CALLBACK',
},
transformResponse: function(data, headersGetter) {
return data.[....].DATA;
}
}

Usually, $resource is used for RESTful APIs. JSONP only uses GET.
So, I would use $http.JSONP instead of $resource for this.
In your code, can you let your server-side support CORS?

Related

Angular 1.x: $resource JSON is returning as a weird object

I have been working with $resource for a little bit now, however it seems tricky to use with JSON return values. I have tried many solutions but it seems to always return it as an object with..less than desirable output as shown in the image below.
When I verify the data is postman I receive something like this:
{
"examples": [
{
"text": "their pet cat"
}
],
...
With this I know the data is being sent properly. However, every solution I try for getting the data back correctly using $resource gives me the weird object you saw in the image above.
I have tried many but I believe these are closest:
Attempt One:
jsonpquery: { method: 'JSONP', params: {callback: 'JSON_CALLBACK'}, isArray: true }
Attempt Two
return $resource('myBackend.php?action=word',
{}, {
query: { method: "GET", isArray: false }
});
Attempt 3:
return $resource('myBackend.php?action=word',
{ get: { method: "JSON", isArray: false}});
This is in a factory and I call it like so:
dictionaryFactory.getWord('cat')
.query().$promise.then(function(value) {
console.log('value value', typeof value);
console.log('value value', value);
});
I am fairly sure I am not setting up my $resource return function correctly
but after a few days of searching and trying solutions on the web I assume I am not understanding how to do it.
I have tried the answers from this post and was unable to solve the issue. The data comes back the same.
Update: I have now tried this as well:
query: {
method: 'GET',
isArray: true,
transformResponse: function(data) {
return angular.fromJson(data).items;
}
}
However this just gives me the following error in console:
Unexpected number in JSON at position 7305 at JSON.parse
I know the data is not the problem as I am using the API From Oxford: https://developer.oxforddictionaries.com/d
Update:
I read some more and tried this however I get am error
Unexpected number in JSON at position 7305
:
return $resource('myBackend.php?action=word', {},
{
jsonpquery: {
method: 'get',
dataType: 'json',
transformResponse: function(data, headers){
return JSON.parse(data);
}
}
});
I also tried this however for some reason then I get a 403. Which I do not understand as when it is a get request it will return the weird object of characters.
return $resource('backend.php?action=oxford', {} {
jsonpquery: {
method: 'JSON',
isArray: true
}
});

Why does $resource save() method's success callback not passed in response data?

I'm very confused by the behaviour of ngResource, when calling a get method, the success callback has plain response data:
svc.userApi().get({id:0})
.$promise.then(
function(response) {
console.log(response);
});
Here response is whatever data returned by backend. However, if I use save/post method:
svc.authenticateApi().save({
username: username,
password: password
})
.$promise.then(
function(val) {
console.log("user token:", val);
});
the val in callback is not the data returned by backend after the post action, instead it's a resource object, with properties like $promise and $resolved, etc:
To get the proper data I have to define a transformResponse option when defining authenticateApi. (See this question for detail). Why is ngResource designed this way?
For comparison, it's very straightforward when using $http for this:
svc.login = function(username, password) {
return $http.post(apiEndpoint + 'authenticate', {
username: username,
password: password
})
.then(function(val) {
console.log("user token:", val.data);
});
};
The val.data in success callback is again a plain response data:
Have I been using save/post the wrong way? In my use case how else would you get authentication token back after successfully authenticated?
Looks like save is expecting an array, for some reason, instead of a string.
I recommend writing your own methods for the resource
var UserResource = $resource('/users/:id', {id: '#id'}, {
'get': {
cache: cache,
isArray: true
},
'saveUserDefinition': {
method: 'POST',
interceptor: interceptor,
isArray: false
},
'updateUserDefinition': {
method: 'PUT',
isArray: false
}
});
I return the methods of the resource in a service
function getAllUsers(params) {
if (!params) {
params = {};
}
return UserResource.get(params).$promise;
}

Get object by id using $resource

I am dealing with a little issue, and need some help.
I have an API setup, works just fine.
Now I want to get a single object using its ID. I have tried using $http, but could't get it to work. So I switched back to $resource. Here is what I have in my service file.
getPlatesInSelectedContainer: function() {
return $resource('/api/v1/platesincontainer/:id', {id: "#id" },{
get: { cache: true, method: 'GET' }
});
},
And in controller I am reaching to function doing so.
CleaningPlatesFactory.getPlatesInSelectedContainer({id : 1}, function(data){
$scope.plates = data;
console.log(data);
})
But this is not returning any results. What I am doing wrong?
UPDATE
Earlier I have tried $http but getting this.
I would still get $http to work. It's as simple as this:
getPlatesInSelectedContainer: function(data) {
return $http.get('/api/v1/platesincontainer/' + data.id);
}
You need to accept the data object in your service function.
Inside the service:
getPlatesInSelectedContainer: function(data) {
return $resource('/api/v1/platesincontainer/:id', {id: data.id },{
get: { cache: true, method: 'GET' }
});
}
Then call it like this:
getPlatesInSelectedContainer({ id: 123 })

How to use requirejs to work with ajax callbacks?

If I have to leverage niceties of jQuery AJAX API and set my own custom settings for each ajax call my app makes like below:
Say I have a page which displays employee information within table by making ajax calls to some API.
define(["jQuery"], function($) {
var infoTable = function (options) {
function init() {
// Provide success callback
options.success_callback = "renderData";
getData();
}
function renderData() {
// This callback function won't be called as it is not
// in global scope and instead $.ajax will try to look
// for function named 'renderData' in global scope.
// How do I pass callbacks defined within requirejs define blocks?
}
function getData() {
$.ajax({
url: options.apiURL,
dataType: options.format,
data: {
format: options.format,
APIKey: options.APIKey,
source: options.source,
sourceData: options.sourceData,
count: options.count,
authMode: options.authMode
},
method: options.method,
jsonpCallback: options.jsonpCallback,
success: options.success_callback,
error: options.error_callback,
timeout: options.timeout
});
}
}
return {
init: init
}
}
How do I achieve this?
I know we can use JSONP request as require calls but that restricts me to using jsonp, making GET requests and all other features $.ajax offers.
This example would let you either use a default success callback, or provide an override, using:
success: options.successCallback || renderData
(The example uses jsfiddle rest URLs - this fact is unimportant, and stripped out the data object to keep the example short)
define("mymodule", ["jquery"], function($) {
function renderData() {
console.log("inside callback");
}
function getData(options) {
$.ajax({
url: options.apiURL,
dataType: options.format,
method: options.method,
jsonpCallback: options.jsonpCallback,
success: options.successCallback || renderData,
error: null,
timeout: options.timeout
});
}
return {
getData: getData
}
});
require(["mymodule"], function(m) {
console.log(m, m.getData({
apiURL: "/echo/json/"
}));
console.log(m, m.getData({
successCallback: function() { console.log("outside callback"); },
apiURL: "/echo/json/"
}));
});
Would print:
GET http://fiddle.jshell.net/echo/json/ 200 OK 263ms
Object { getData=getData()} undefined
GET http://fiddle.jshell.net/echo/json/ 200 OK 160ms
Object { getData=getData()} undefined
inside callback
outside callback

Triggering a Javascript callback after a completed ajax call within an object

I'm trying to trigger an action after a Javascript object has been created via an AJAX call. My object looks something like this:
function API(uid,accessToken){
$.ajax("path/to/file", {
type: "POST",
data: { user: uid, auth: accessToken },
dataType: "json",
success: function(jsonData) {
arrayname = jsonData[values]
}
});
}
I tried to use JQuery's $.when function to do a callback after the object setup is complete (ie. the array is populated with the ajax response), which looked like this:
$.when( API = new API(uid, accessToken) ).then(function() {
...success function...
});
...but the $.when function triggers with the arrayname values still undefined. From the function's standpoint the deferred object is resolved even though the object values have not yet been set. I've since tried a number of ways to make the API object become deferred based on the completing of the entire ajax call and the setting of the variables, but I'm a bit stuck on the best way to do this.
Any pointers would be most appreciated! Thanks.
You could pass the callback function when you create the object, like so:
function API(uid,accessToken, callback){
$.ajax("path/to/file", {
type: "POST",
data: { user: uid, auth: accessToken },
dataType: "json",
success: function(jsonData) {
arrayname = jsonData[values]
callback(jsonData[values])
}
});
}
and then instantiate the object like so
var api = new API(uid, accessToken, function(array) {
// success function
});
If the problem is due to the "success" callback running after the "then" callbacks, you could try turning success callback into a then callback as well. I don't use JQuery but I guess it would look something like:
function API(uid,accessToken){
return $.ajax("path/to/file", {
type: "POST",
data: { user: uid, auth: accessToken },
dataType: "json",
}).then(function(jsondata){
arrayname = jsondata[values]
});
}
$.when( API = new API(uid, accessToken) ).then(function() {
// ...
});
The reason you use $.when is when you are correlating the callbacks of multiple promises, async tasks, etc. Since jQuery 1.5, all calls to $.ajax and all the wrappers ($.get and $.post) all return promises. Therefore you don't need to wrap this call with the $.when statement unless you want to do $.when(ajaxCall1, ajaxCall2).
Since you want to filter the result from the server, you should use the pipe method of promises:
function API(uid, accessToken)
return $.post(
type: 'POST'
,data: { user: uid, auth: accessToken }
,dataType: 'json'
)
.pipe(function(json) {
return json[values];
})
;
}
This allows you to write your code the way you desire:
API(uid, token)
.then(
// success state (same as promise.done)
function(arrayname /* named from your sample script*/) {
alert('success! ' + arrayname);
}
// error state (same as promise.fail)
,function(jqXHR, status, error) {
console.warn('oh noes!', error);
}
)
.done(function() { /* done #2 */ })
.fail(function() { /* fail #2 */ })
;
Note: promise.pipe() also allows you to filter (change the data passed to) the error callback as well.

Categories