Please find below the angularjs factory method to call http request:
var HttpService = angular.module("HttpService",[]);
HttpService.factory("HttpServiceFactory",['$http', '$q', '$location', '$rootScope' ,function($http, $q, $location, $rootScope){
return {
getData: function(url, headers, bOnErrorRedirect, bShowInPageError, params){
var headerParam = {'Accept':'application/json'};
if(headers !== undefined || headers !== null){
headerParam = $.extend(headerParam, headers);
}
var updatedParams = {'TimeStamp':new Date().getTime()};
updatedParams = $.extend(params, updatedParams);
var deferred = $q.defer();
$http.get(url,{
headers: headerParam,
params : updatedParams
}).success(function(successResponse){
if(successResponse){
var responseJSON = angular.fromJson(successResponse);
if(responseJSON && responseJSON.messages && responseJSON.messages.length){
//Process Error
}else{
deferred.resolve(successResponse);
}
}else{
deferred.resolve(successResponse);
}
}).error(function(errorResponse , status){
//Process Error
console.error("status here:: "+status);
});
return deferred.promise;
}
}
}]);
And I am calling this method in controller with all required dependencies as below:
HttpServiceFactory.getData(args.sURL,null,false,true,args.oQueryParams).then(function(response){
scope.bDataLoading = false;
// process data
})
.catch(function(oResponse) {
scope.bDataLoading = false;
scope.bDisplayError = true;
// process error
});
Here everything works fine. But the issue is when I've multiple http calls on a page, the UI freezes and does not allow to interact till the request has been processed.
For example, on a page I am displaying 2 angular-ui-grid based on user's selected criteria by input box and calendar control. In such case, the UI freezes until both grids have been displayed or error message has been displayed.
During http service call, user can not do anything but simply wait to finish the request.
How do I resolve the issue of UI freezing ? Is it a true async behavior ? If not, what am I missing to achieve correct async behavior ?
Related
I'm developing a single page application. I am making use of Angularjs.v1.2.28. I'm making a HTTP GET request to the backend using this code.
return {
getCategories : function(sessionid,terminalid,tableno,section){
var req = {
method: 'GET',
url: Config.url+ "/menucategories",
params : {
'sessionid' : sessionid,
'terminalid' : terminalid,
'tableno' : tableno,
'section' : section
}
};
return $http.get(req);
},
I make use of the promise object that is returned from service in controller.
var categoryPromise = categoryService.getCategories(sessionid,terminalid,tableno,section);
categoryPromise.then(function(payload){
var categories = payload.data;
if(categories.status.code == "1"){
if(Object.prototype.toString.call(categories) === '[object Array]') {
$scope.categories = categories;
categoryService.setCategories(categories);
$scope.pax = tableService.getPax();
$scope.tablechair = tableService.getChoseTableChair();
}
}
else{
$location.url("/login");
$scope.errorMsg = categories.status.desc;
}
},function(errorPayload){
$location.url("/login");
$scope.errorMsg = "Server error while processing the request.Please contact system administrator";
});
It's always the errorCallback is getting called due to the URL getting changed to the browser application URL appended with some malformed characters. The URL which i give is
http://localhost:8080/CafexRestful/menucategories
But, it gets changed to the browser application URL below
http://localhost:8080/CafexMobile/[object%20Object]
I have been debugging in Chrome and Firebug. I couldn't resolve it. It may be something which is happening under the hood. The same code is working with another controller and service, where i fetch a different data. Please let me know if you need anymore information. Thanks.
$http.get in angularjs needs an url string. You should use url string instead of an object
Using $http.get function:
return {
getCategories : function(){
return $http.get("/menucategories"); // using $http.get function.
},
Using $http function.
return {
getCategories : function(sessionid,terminalid,tableno,section){
var req = {
method: 'GET',
url: Config.url+ "/menucategories",
params : {
'sessionid' : sessionid,
'terminalid' : terminalid,
'tableno' : tableno,
'section' : section
}
};
return $http(req); //using $http function only.
},
Please see the document: https://docs.angularjs.org/api/ng/service/$http
I would like my AngularJS app to capture "no internet connection" just like how Chrome captures it. When Chrome captures it, it shows net::ERR_INTERNET_DISCONNECTED on the console.log.
Angular's HTTP interceptor is not able to capture it. The only data I get is
rejection.data.status undefined
rejection.status 0
So far, this has been working great. I noticed that the status is 0 when it can't contact anything. This is inside my http interceptor
responseError: function (rejection) {
if (rejection.status == 0) {
// put whatever behavior you would like to happen
}
// .......
return $q.reject(rejection);
}
A simple script that could help you, you could do it in different ways, even loading an image and call a function when this fails.
function getNetworkStatus(callback, timeout, x){
x = new XMLHttpRequest();
x.timeout = timeout,
x.onreadystatechange = function(){
x.readyState == 4 && callback(x.status == 200)
}, x.onerror = function(e){
callback(!1)
}, x.ontimeout = function(){
callback(!1)
}, (x.open("GET", "http://ip-api.com/json/"), x.send());
}
getNetworkStatus(function(isOnline){
console.info(isOnline ? "ONLINE" : "OFFLINE");
},60000);
UPDATE
We define this interceptor in httpProvider, so return strictly an error when a call does not happen successfully
angular.module('MyApp', []).config([
'$httpProvider',
function($httpProvider) {
$httpProvider.interceptors.push([
'$q',
function($q) {
return {
responseError: function(res){
console.info("Failed to open url: " + res.config.url, res);
//Angular returns "success" by default, but we will call "error" if data were not obtained.
if(res.data == null && res.status === 0 && res.statusText === ""){
return $q.reject(res) //callback error()
}
return res //return default success()
}
};
}
]);
}
]).run(['$http', function($http) { // --TEST--
//$http.get("http://ip-api.com/json/").success(function(){ //page online
$http.get("https://ip-api.com/json/").success(function(){ //try "https" (page offline to test)
console.info("My great page is loaded - We are online :)",arguments)
}).error(function(){
console.info("ERROR: My great online page is not loaded :/ - possibility of connection loss?",arguments)
});
}]);
You can change a trusted URL that you think will never be offline, for example Google, but remember, the url should have origin access permissions that does not show the "Access-Control-Allow-Origin" error. http://ip-api.com/json/ is ok.
I believe this has to do with the way JS Closures work, but I am not totally sure. I am using an AngularJS service to manage the life-cycle of a model that is used within my application. The service uses a combination of fetch() and save() to run GET and POST requests get and update the model from an API. After I fetch() the object, I attempt to place the result into an object sitting in the service where it can be fetched later on. My problem, is that after a successful save(), I take the result and place it into the same object to essentially "update" the object that is on the client with the correct object that is on the server (hence the result of the POST is just an echo of the payload if all is successful).
The problem is that my object is not persisting, and all subsequent calls to save() contain a "stale" object that is not completely updated.
Here is my service:
app.factory('MailboxSubscription', function (API, $q, $stateParams, $rootScope) {
var Subscription = null; //THIS IS MODEL THAT I TRY TO UPDATE CONSTANTLY
var isBusy = false;
var service = {};
var deferred;
var defaultFailure = function(res){
}
service.fetch = function (success, force, failure) {
if(!failure){ failure = defaultFailure;}
if(isBusy){
deferred.promise.then(success, failure);
return deferred.promise;
}else{
deferred = $q.defer();
}
if(Subscription && !force){ // ONCE THE MODEL HAS BEEN FETCHED ONCE, IT STAYS IN MEMORY AND ALL SUBSEQUENT CALLS WILL SKIP THE API CALL AND JUST RETURN THIS OBJECT
deferred.resolve(Subscription);
}else{
//Make the API call to get the data
//Make the API call to get the data
if(typeof(success) === 'function'){
var ServiceId = $stateParams.serviceId;
}else{
var ServiceId = success;
}
isBusy = true;
API.Backups.O365.Exchange.get({id : ServiceId || $stateParams.serviceId}, function(res){
isBusy = false;
if(res.success){
Subscription = res.result; // ON A FIRST-TIME FETCH, THIS API CALL IS USED TO GET THE MODEL
deferred.resolve(Subscription);
}else{
deferred.reject(res);
}
}, function(res){
isBusy = false;
deferred.reject(res);
});
}
deferred.promise.then(success, failure);
return deferred.promise;
}
service.save = function(success, failure){
if(!failure){ failure = function(){};}
if(!success){ success = function(){};}
var deferred = $q.defer();
API.Backups.O365.Exchange.update({id :$rootScope.ServiceId || $stateParams.serviceId}, Subscription, function(res){
if(res.success){
Subscription = res.result; // AFTER AN UPDATE IS MADE AND THE OBJECT IS SAVED, I TRY TO SET THE RESULT TO Subscription.
deferred.resolve(res);
}else{
deferred.reject(res);
}
}, function(res){
deferred.reject(res);
});
deferred.promise.then(success, failure);
return deferred.promise;
}
service.get = function(){
return Subscription;
}
return service;
});
So the problem appears to stem from trying to use Subscription as a centralized resource for storing the model, but the model is not updating correctly.
If you are looking to have that Subscription model updated throughout the service, I'd suggested when you call MailboxSubscription.fetch() and MailboxSubscription.save()in your controller, you use MailboxSubscription.get() in the .then() method of your calls.
// get initial value of Subscription model
$scope.Subscription = MailboxSubscription.get();
// let's fetch
MailboxSubscription.fetch().then(
// callback
function() {
// let's get the updated model
$scope.Subscription = MailboxSubscription.get();
},
// errback
function() {
// handle error
}
);
// let's save
MailboxSubscription.save().then(
// callback
function() {
// let's get the updated model
$scope.Subscription = MailboxSubscription.get();
},
// errback
function() {
// handle error
}
);
Also, I've created a working jsfiddle simplifying your use case. It works fine. Maybe there is something that can be gleamed from that (I am using $timeout to spoof your API calls).
I have built a simple application in Angular consuming a simple API I created myself using Laravel. The application is hosted here. The API is hosted here. Now I can log in to the application at which point the API returns a simple auth_token which is sent as the URL parameter in every subsequent request that is sent to the server.
There is only one user in the system:
Email: admin#admin.com
Password: admin12345
You can log into the application using these credentials at which point the application will set a cookie using the $cookieStore service and will use the token in this cookie for every subsequent request. After using the application, a user can log out from the application, where a DELETE request is sent to the server and on the success method, the cookie is deleted from the browser.
Unfortunately there is some issue with the code I suppose. The DELETE request is working as expected and it deletes the auth_token on the server and returns 200 OK. But the success method is not called. I am not sure what I am doing wrong. It might be just a syntax problem.
app.js
function AppCtrl ($scope, $cookieStore, $location, Auth) {
$scope.setActive = function (type) {
$scope.destinationsActive = '';
$scope.flightsActive = '';
$scope.reservationsActive = '';
$scope[type + 'Active'] = 'active';
};
$scope.authenticate = function (credentials) {
Auth.save(credentials, function(data){
$cookieStore.put('auth_token', data.auth_token);
$scope.isLoggedIn = true;
$location.path('destinations');
$scope.message = null;
}, function(data){
$scope.message = "Email/Password combination incorrect!";
});
};
$scope.logout = function () {
//var auth_token = $cookieStore.get('auth_token');
Auth.delete({
'auth_token': $cookieStore.get('auth_token')
}, function(data){
$scope.isLoggedIn = false;
$cookieStore.remove('auth_token');
});
};
if($cookieStore.get('auth_token')){
$scope.isLoggedIn = true;
}else{
$scope.isLoggedIn = false;
}
}
The logout function is called when the log out button is pressed. What am I doing wrong here?
Note: The application is not working on Chrome for some reason (Use Firefox). If you can shed some light on that, it would be very helpful.
Both the repositories are public if you wish to have a look:
AngulAir Application: http://gitlab.learningtechasia.com:8901/rohan0793/angulair.git
AngulAirAPI: http://gitlab.learningtechasia.com:8901/rohan0793/angulairapi.git
Here is your solution
$scope.logout = function () {
//var auth_token = $cookieStore.get('auth_token');
Auth.delete(
{'auth_token': $cookieStore.get('auth_token')}, // parameters
{},//postData, which you don't need for this
function(data){
$scope.isLoggedIn = false;
$cookieStore.remove('auth_token');
},
// error callback
function (httpResponse) {
// do what you want for error handling here
}
);
};
Note:-> (Below points solved the problem)
Only the 2nd option(postdata) in $resource.delete API was missing. We should give it as a blank {} if it is not required for API.
And delete method should return 204 Status Code in order to execute success callback.
I'm implementing a server with WCF and trying to reach its rest services using a client developed with Angular.
Here is the service :
public interface IConnexion
{
[OperationContract]
[WebGet(UriTemplate = "Connexion/{login}/{mdp}", ResponseFormat = WebMessageFormat.Json)]
Utilisateur Connexion(string login, string mdp);
[OperationContract]
[WebInvoke(UriTemplate = "Deconnexion/{id_user}", RequestFormat = WebMessageFormat.Json)]
void Deconnexion(string id_user);
}
And my function trying to reach the service :
app.service('serverConnexionServices', function(userService, serverConfigService) {
this.Connexion = function(login, pass)
{
var uri = this.getServerUri() + "Connexion/"+login+"/"+CryptoJS.MD5(pass);
var promises = $http.get(uri)
.success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
return data;
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
return "";
});
return promises;
};
});
my form's controller:
app.directive('loginContent', function(userService, serverConnexionServices){
return{
restrict : 'E',
templateUrl : "includes/home/login-content.html",
controllerAs: "loginController",
controller: function($scope, userService, serverConnexionServices) {
var IsSubmitEnabled = true;
this.errors = "";
this.validateLogin = function(user) { // Calling the service
this.IsSubmitEnabled = false; // To avoid more than one click while checking on the server
var retour = serverConnexionServices.TryConnection(user).then(function(promise) {
alert('promise:'+retour);
if(promise.length > 0)
{ // There is an error so we show a message to the user
this.promise= retour;
}
this.IsSubmitEnabled = true;
user = {};
});
};
}
}
});
But when I try to open my client the alert box appears before the breakpoint is raised in Visual Studio (I put the breakpoint on the first line of my "Connexion" function. So it means the server has not answered yet to the request but the $http.get() has already sent its answer. So the result object doesn't contain what the server is sending. According to Angular's documentation, the response will be sent asynchronously. But How can I force my request to wait the answer ?
My controller now gets the answer from the server but it doesn't update the view, and if I try to call manually $scope.$apply() I get the error "$digest already in progress". How can I force my view to refresh itself ? (I noticed that if I put this.errors = "aaa"; once the "then" is closed, my view is refreshed and whos "aaa", I've also checked that errors.length > 0 with an alert message)
My view :
<div ng-show="loginController.errors.length > 0" class="alert alert-danger" role="alert" style="display: inline-block;"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>{{loginController.errors}}</div>
EDIT: I can now reach my breakpoint in the implementation of Connexion in Visual Studio but still the answer from the server is not what is expected
EDIT2 : I've added my controller which needs the refresh.
EDIT3 : Ok, I updated my code, I needed to put a "then" function on my controller's call and my controller now get the answer. However my view doesn't update itself on errors. It should set the div visibility to true but the async call seems to block it. I've tried to use apply() and digest() without success. Could you help me on this ?