I am trying to set up a service that, when I feel like it, I can flip to live data coming from an API. The getData function takes skip/take parameters to define start record and number of records.
My data is currently in a json file:
{
"Data":[{
"Id":"1462",
"Name":"Cynthia"
},{
"Id":"1463",
"Name":"Bob"
},{
...
}],
"Total":71
}
My service currently pulls all json data at once:
var _getData = function (optionsData) {
return $http({
method: 'GET',
url: 'data/packages.json'
})
.then(function successCallback(response) {
return response;
},
function errorCallback(response) {
return response;
});
}
It seems to me that I have to write the paging logic right into the service:
.then(function successCallback(response) {
var records = response.data.Data;
var firstRecord = 0;//optionsData.skip;
var numberOfRecords = 1;//optionsData.take;
response.data.Data = records.slice(firstRecord, firstRecord + numberOfRecords);
return response;
},
Is this the right way in principle?
[ UPDATE ] The controller method:
var getPackageData = function (options){
return dataService.getData(options.data).then(
function successCallback(response) {
options.success(response.data.Data);
$scope.totalCount = response.data.Total;
},
function errorCallback(response) {
// handle error
}
);
};
my errorCallback is wrong? How?
The errorCallback is converting a rejected promise to a fulfilled promise.
To avoid conversion, either throw the response or return $q.reject:
var _getData = function (optionsData) {
return $http({
method: 'GET',
url: 'data/packages.json'
})
.then(function successCallback(response) {
return response;
},
function errorCallback(response) {
//To avoid converting reject to success
//DON'T
//return response;
//INSTEAD
throw response;
//OR
//return $q.reject(response);
});
}
Another common error is to fail to include either a throw or return statement. When a function omits a return statement, the function returns a value of undefined. In that case, the $q service will convert rejected promises to fulfilled promises which yield a value of undefined.
Related
I have a Service which fetch the data from API.When I am trying to call this service. It's returning with same value.
appName.service('FetchCustomerDate', ['$http', '$q', function($http, $q) {
var self = this;
self.getCustomerData = function(token,name) {
var deferred = $q.defer();
return $http({
method: 'GET',
url: ,
headers: {
"Authorization": token,
"x-xcmc-auth": ''
}
}).then(function(response) {
deferred.resolve(response);
return deferred.promise;
}, function(response) {
deferred.reject(response);
return deferred.promise;
});
};
}]);
I see a bit of confusion here. Let's try to clear it. If you want to use deferred object, you need to change your code a bit:
appName.service('FetchCustomerDate', ['$http', '$q', function ($http, $q) {
var self = this;
self.getCustomerData = function (token, name) {
var deferred = $q.defer();
$http({ // Do not return here, you need to return the deferred.promise
method: 'GET',
url: '...some URL here...',
headers: {
"Authorization": token,
"x-xcmc-auth": ''
}
}).then(function (response) {
deferred.resolve(response); // It's correct, you are resolving the deferred promise here.
// return deferred.promise; // You do not need to return the deferred.promise here.
}, function (response) {
deferred.reject(response); // It's correct, you are rejecting the deferred promise here.
// return deferred.promise; // You do not need to return the deferred.promise here.
});
return deferred.promise; // The function must return the deferred.promise
};
}]);
In detail, function getCustomerData must return the promise belonging to deferred object with return deferred.promise. Inside then() callback you simply resolve or reject deferred promise. You do not need to return the deferred.promise.
You can improve the code. The $http service returns a promise, and the value returned by then callbacks is wrapped by then method in a promise. Knowing that, you can remove the use of deferred object:
appName.service('FetchCustomerDate', ['$http', function ($http) {
var self = this;
self.getCustomerData = function (token, name) {
return $http({ // Here, you need to return the promise returned by $http. Than promise will contain the response returned inside "then" callbacks.
method: 'GET',
url: '...some URL here...',
headers: {
"Authorization": token,
"x-xcmc-auth": ''
}
}).then(function (response) {
return response; // Simply return the response, it will be wrapped in a resolved promise by "then()"
}, function (response) {
return response; // Simply return the response, it will be wrapped in a rejected promise by "then()"
});
};
}]);
As you can see, the 2 then callbacks simply returns the response object, for this reason you can omit them:
appName.service('FetchCustomerDate', ['$http', function ($http) {
var self = this;
self.getCustomerData = function (token, name) {
return $http({ // Here, you need to return the promise returned by $http. Than promise will contain the response form the GET call
method: 'GET',
url: '...some URL here...',
headers: {
"Authorization": token,
"x-xcmc-auth": ''
}
});
};
}]);
Usually when you fetch data with the $httpservice, you want to obtain data from the response and affect it to the $scope for instance, or process it somehow. What are you trying to do? Please clarify your question.
Normally a fetch will look something like this:
appName.service('FetchCustomerDate', ['$http', '$q', function($http, $q) {
var self = this;
function notifyError(reason) {
console.error(reason);
}
self.getCustomerData = function(token,name) {
var deferred = $q.defer();
return $http({
method: 'GET',
url: ,
headers: {
"Authorization": token,
"x-xcmc-auth": ''
}
})
.then(function onSuccess(response) {
var cfg = response.data; // process data
})
.then(function onSuccess(response) {
// chained promises
})
.then(
function onSuccess(res) {
// ... this will trigger the chain reaction
deferred.resolve(res);
},
function onFailure(reason) {
notifyError(reason); // manage the error
deferred.reject(reason);
})
;
return deferred.promise;
}
}]);
I want to access to response successCallback(response)
var list_of_user = $http({
method: 'GET',
url: '/users/'
}).then(function successCallback(response) {
$scope.all_users = response.data;
}, function errorCallback(response) {
console.log(response);
});
console.log("$scope.all_users ", $scope.all_users)
is undefiend
I can access $scope.all_users from html, but how can I access to $scope.all_users in controller?
$http is async and console.log is executed before actually request is completed.
As you defined in comment that you want to compare two responses you can do that by simply putting a flag that will wait for both requests to finish.
var done = 0;
var onRequestFinishes = function() {
done += 1;
if (done < 2) {
return; // one of both request is not completed yet
}
// compare $scope.response1 with $scope.response2 here
}
and send first request and save response to $scope.response1 and invoke onRequestFinishes after that.
$http({
method: 'GET',
url: '/users/'
}).then(function successCallback(response) {
$scope.response1 = response.data;
onRequestFinishes();
}, function errorCallback(response) {
console.log(response);
});
Similarly send second request
$http({
method: 'GET',
url: '/users/'
}).then(function successCallback(response) {
$scope.response2 = response.data;
onRequestFinishes();
}, function errorCallback(response) {
console.log(response);
});
For request handling you can create individual promise chains and use $q.all() to execute code when all promises are resolved.
var request1 = $http.get('/users/').then(function(response)
{
console.log('Users: ', response.data);
return response.data;
}
var request2 = $http.get('/others/').then(function(response)
{
console.log('Others: ', response.data);
return response.data;
}
$q.all([request1, request2]).then(function(users, others)
{
console.log('Both promises are resolved.');
//Handle data as needed
console.log(users);
console.log(others);
}
This code fetches categories and give them to controller.
sampleApp.factory('SCService', function($http, $q) {
var SuperCategories = [];
var SCService = {};
SCService.GetSuperCategories = function() {
var req = {
method: 'POST',
url: SuperCategoryURL,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: "action=GET"
};
if ( SuperCategories.length == 0 ) {
return $http(req).then(function (response) {
SuperCategories = response.data;
return SuperCategories;
});
}else {
return $q.when(SuperCategories);
}
}
return SCService;
});
I think code is perfect until there is no error in http request.
My query is how to do error handling (try catch or something like that), in case if server have some issue or may be cgi-script have some issue and not able to server the request.
Angular promises use a method catch for that.
return $http(req).then(function (response) {
SuperCategories = response.data;
return SuperCategories;
}).catch(function(error) {
// Do what you want here
});
You should use also finally :
return $http(req).then(function (response) {
SuperCategories = response.data;
return SuperCategories;
}).catch(function(error) {
// Do what you want here
}).finally(function() {
// Always executed. Clean up variables, call a callback, etc...
});
Write like
return $http(req).then(function (response) {
//success callback
},
function(){
//Failure callback
});
Use callback methods from controller Like
Controller.js
service.GetSuperCategories(function (data) {console.log('success'},function (error){console.log('error'});
service.js
sampleApp.factory('SCService', function($http, $q) {
var SuperCategories = [];
var SCService = {};
SCService.GetSuperCategories = function(successMethod,errorMethod) {
var req = {
method: 'POST',
url: SuperCategoryURL,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: "action=GET"
};
return $http(req).then(successMethod(data),
errorMethod(error));
}
return SCService;
});
You can use the .success and .error methods of $http service, as below
$http(req).success(function(data, status, headers){
// success callback: Enters if status = 200
}).error(function(status, headers){
// error callback: enters otherwise
});
I have a method seatClicked() that calls getUserID() to get the user id corresponding to the session atribute 'user'. There is a table that contains the username and user id(unique). The following is the definition of seatClicked()
$scope.seatClicked = function() {
promise = $scope.getUserID();
promise.then(function(results){
$scope.seatID.userID = results; // store the user id in $scope.seatID.userID
});
}
This is the definition for getUserID()
$scope.getUserID = function() {
var deferred = $q.defer();
$http({
method : 'POST',
url : 'http://localhost:8080/AirlineApp/getUserID',
headers : {
'Content-Type' : 'application/json'
}
}).then(function(data){
alert("userID:"+data)
deferred.resolve(data);
})
return deferred.promise;
};
The variable 'results' returned by $http.then() is always undefined, whereas if I use $http.success() I am able to retrieve the user id.
I want to fetch the user id before further processing. Is there any way for the function to wait till the data is fetched from the database?
P.S. I tried callback as well, no luck.
Edit: I am able to fetch the id and store it (thanks all), but it takes much longer than the time taken to execute my next operation(not presented here). Can I stall that operation till I am assured of the id?
what we did in our project, added PromiseUtils service for any REST calls
.service("PromiseUtils", function($q) {
return {
getPromiseHttpResult: function (httpPromise) {
var deferred = $q.defer();
httpPromise.success(function (data) {
deferred.resolve(data);
}).error(function () {
deferred.reject(arguments);
});
return deferred.promise;
}
}
})
and use it nice and easy
var anyCall = $http({
method: 'POST',
url: 'http://localhost:8080/AirlineApp/getUserID',
headers: {
'Content-Type': 'application/json'
}
});
PromiseUtils.getPromiseHttpResult(anyCall).then(function(result){
console.log("result", result);
})
or
PromiseUtils.getPromiseHttpResult($http.get('/api/get/call'))
.then(function(result){
console.log("result", result);
})
PromiseUtils.getPromiseHttpResult($http.post('/api/post/call', data))
.then(function(result){
console.log("result", result);
})
if you need error() function, just add as second parameter
PromiseUtils.getPromiseHttpResult($http.get('/api/get/call'))
.then(function(result){
console.log("result", result);
}, function (arguments) {
console.log("fail", arguments);
})
$http returns a special promise different from the standard promise like here https://github.com/promises-aplus/promises-spec
If you use $http you need to retrieve your data like this :
$http
.success(success)
.error(error) ...
But you can use standard promise by wrapping the $http promise in your service using $q like this :
var defer = $q.defer();
var http = $http({
url: SharedFactory.endPoint + '/stats',
method: 'GET',
params: params
});
http.success(function(stats) {
defer.resolve(stats);
})
.error(function() {
defer.reject("Failed to get stats");
});
return defer.promise;
I have created a factory, to handle all my http related calls. It returns following inline code:
return {
get: function (opts) {
var deferred = $q.defer();
var def = defaultOptions(HttpMethod.Get);
$.extend(def, opts);
$http({ method: 'get', url: config.remoteServiceName + def.url }).then(function (result, status, header) {
def.success(deferred, { data: result });
}, function (data) {
def.error(deferred, data);
});
return deferred.promise;
},
post: function (opts) {
var deferred = $q.defer();
var def = defaultOptions(HttpMethod.Post);
$.extend(def, opts);
$http.post(config.remoteServiceName + def.url, def.data).then(function (result) {
def.success(deferred, result);
}, function (data) {
def.error(deferred, data);
});
return deferred.promise;
},
remove: function (opts) {
var deferred = $q.defer();
var def = defaultOptions(HttpMethod.Delete);
$.extend(def, opts);
$http({ method: 'delete', url: config.remoteServiceName + def.url }).then(function (result) {
def.success(deferred, result);
}, function (data) {
def.error(deferred, data);
});
return deferred.promise;
}
};
Now, when i am making the calls, if there are few parallel calls being made, all promise resolution is getting mixed up. I am getting the resultset from one request in another's resolution.
Not able to solve the problem. What am i doing wrong?
Issue was not of parallel calls. It was somewhere else. JS model for ajax execution works fine, and no issue with $http either. We were using WebAPI as backend, and some of our global filters had state, which got altered on each call. Hence the data was mixed up. No issue on client end.
Thanks everyone