Since I am using ajax request using $http. It takes a long time since my operation on server takes time. I need to show loader while processing request, but the loader does not show. Although my code seems correct. I tried different methods but did not work.
Index.html
<body ng-app="app">
<!-- loader, can be used on multiple pages-->
<div class="loading loader-quart" ng-show="isLoading"></div>
<!-- my logic -->
</body>
addCtrl.js
//method to get all the attributes and send to server using service
$scope.add = function () {
if ($scope.Option == 'newInstance')
$scope.singleObject.FK_Name = 'MetisEmptyTemplate';
$rootScope.isLoading = true;
var featuresList = websiteService.getUpdatedTree($scope.treeDataSource);
var formData = new Website("", $scope.singleObject.Name, $scope.singleObject.DisplayName, $scope.singleObject.Description, $scope.singleObject.State, "", $scope.singleObject.FK_Name, $scope.singleObject.Email, featuresList);
websiteService.addwebsite(formData);
$rootScope.isLoading = false;
}
websiteService.js
//service to add website
this.addwebsite = function (website) {
$http({
method: 'POST',
url: $rootScope.url + 'Add',
data: JSON.stringify(website),
contentType: 'application/json'
}).success(function (data) {
alert(data);
}).error(function (data, status, headers, config) {
//alert(data);
});
}
Since I am going to turn isLoading as "true" in start and then after request completes I turn isLoading "false". Where is the problem in code?
Your websiteServices code gets executed asynchronously. Which means that the above code would display the loader and then pretty much hide it again instantly.
To handle async code in the controller you must return a promise from the service and put the hiding of the spinner in a callback function using .then().
service:
this.addwebsite = function (website) {
var deferred = $q.defer();
$http({
method: 'POST',
url: $rootScope.url + 'Add',
data: JSON.stringify(website),
contentType: 'application/json'
}).success(function (data) {
alert(data);
deferred.resolve(data);
}).error(function (data, status, headers, config) {
//alert(data);
deferred.reject(data);
});
return deferred.promise
}
controller:
websiteService.addwebsite(formData).then(function(){
$rootScope.isLoading = false
});
this.insertMliveResponse = function(data){
var defer=$q.defer();
var requestURL='/mlive-portlet/rest/mliveResponseService/insertmLiveResponse';
httpRequest(requestURL,data).then(function(data){
defer.resolve(data.data);
},function(data){
defer.reject(data.data);
})
return defer.promise;
}
If you are making request then,
I think the best way to show hide loader is interceptor
In my snippet, I am using loader service to activate/deactivate loader
For Eg:
// http.config.js file
export function httpConfig($httpProvider, AuthInterceptProvider) {
'ngInject';
AuthInterceptProvider.interceptAuth(true);
// added for show loader
$httpProvider.interceptors.push(function (loaderService, $q) {
'ngInject';
return {
'request': function (config) {
loaderService.switchLoaderModeOn();
return config;
},
'requestError': function (rejection) {
loaderService.switchLoaderModeOff();
return $q.reject(rejection);
},
'response': function (response) {
loaderService.switchLoaderModeOff();
return response;
},
'responseError': function (rejection) {
loaderService.switchLoaderModeOff();
return $q.reject(rejection);
}
};
});
}
// and in your module.js file
import {httpConfig} from './config/http.config';
.config(httpConfig)
Related
I am doing some http calls in Angular and trying to call a different service function if an error occurs. However, regardless of my original service call function return, the promise it returns is always "undefined". Here is some code to give context:
srvc.sendApplicantsToSR = function (applicant) {
var applicantURL = {snip};
var promise = $http({
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST',
url: applicantURL,
data: applicant
})
.success(function (data) {
return data;
})
.error(function (error) {
return [];
});
return promise;
};
Then, in the controller:
for (var applicant in $scope.applicants) {
$scope.sendATSError($scope.sendApplicantsToSR($scope.applicants[applicant]), applicant);
}
$scope.sendATSError = function (errorCheck, applicantNumber) {
if (angular.isUndefined(errorCheck)) {
console.log(errorCheck);
AtsintegrationsService.applicantErrorHandling($scope.applicants[applicantNumber].dataset.atsApplicantID);
}
};
However, it is always sending errors because every response is undefined. How can I differentiate between the two returns properly? Thank you!
Looking at angular documentation, the sample code is
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
based on that - your first code snippet should be
srvc.sendApplicantsToSR = function(applicant) {
var applicantURL = {
snip
};
return $http({
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST',
url: applicantURL,
data: applicant
});
};
As others have said, $http's .success() and .error() are deprecated in favour of .then().
But you don't actually need to chain .then() in .sendApplicantsToSR() as you don't need (ever) to process the successfully delivered data or to handle (at that point) the unsuccessful error.
$scope.sendApplicantsToSR = function (applicant) {
var applicantURL = {snip};
return $http({
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
method: 'POST',
url: applicantURL,
data: applicant
});
};
Now, in the caller (your line of code in the for loop), a promise is returned (not data) and that promise will, on settling, go down its success path or its error path. Exactly what happens on these paths is determined entirely by the callback functions you write in one or more chained .thens .
So what you need to write is a kind of inside-out version of what's in the question - with $scope.sendApplicantsToSR() on the outside and $scope.sendATSError() on the inside - and linked together with a .then().
for (var prop in $scope.applicants) {
var applicant = $scope.applicants[prop];
$scope.sendApplicantsToSR(applicant).then(null, $scope.sendATSError.bind(null, applicant));
}
// Above, `null` means do nothing on success, and
// `function(e) {...}` causes the error to be handled appropriately.
// This is the branching you seek!!
And by passing applicant, the error handler, $scope.sendATSError() will simplify to :
$scope.sendATSError = function (applicant) {
return AtsintegrationsService.applicantErrorHandling(applicant.dataset.atsApplicantID); // `return` is potentially important.
};
The only other thing you might want to know is when all the promises have settled but that's best addressed in another question.
You should return your promisse to be handled by the controller itself.
Simplifying:
.factory('myFactory', function() {
return $http.post(...);
})
.controller('ctrl', function(){
myFactory()
.success(function(data){
// this is your data
})
})
Working example:
angular.module('myApp',[])
.factory('myName', function($q, $timeout) {
return function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve('Foo');
}, 2000);
return deferred.promise;
}
})
.controller('ctrl', function($scope, myName) {
$scope.greeting = 'Waiting info.';
myName().then(function(data) {
$scope.greeting = 'Hello '+data+'!';
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="ctrl">
{{greeting}}!
</div>
How would I go about setting a function for all .error calls from the $http service wherever used.
Throughout my app I have used many $http calls wrapped in a service and would like to avoid duplication of implementing all .error methods.
e.g.
postTest: function (data) {
var url = '/test';
return $http({
method: 'POST',
url: url,
data: data
}).
success(function (data) {
return data;
}).
error(function (status) { //Avoid duplication of this throughout all $http calls.
if (status === 404) {}
return status;
});
}
you could create and interceptor to catch all the errors
so something like this
var applicationWideInterceptors = function ($q, toastrSrvc) {
return {
responseError:function(rejection) {
//do something with the rejection
if (rejection.status === 404) {
toastrSrvc.error(rejection.data);
}
return $q.reject(rejection);
}
}
};
app.factory("appWideIntercpt", ["$q", "toastrSrvc", applicationWideInterceptors]);
dont forget to add it to your config block
$httpProvider.interceptors.push("appWideIntercpt"
I have the following function named getvalue. It is inside an AngularJS module together with a controller. I am trying to call this function on click event invoking another function in a controller.(I hope I am clear)
function:
function getvalue(Data, $http) {
var value=undefined;
$http({
url: /myurl,
method: "GET",
params: {
tmp: Data.tmp,
pressure: Data.pressure
}
}).success(function (data, status, headers, config) {
value=parseFloat( console.log(data));//console working here
return value;
});
return value;
}
Code inside Controller
value= getvalue(formData, $http );
alert(value);//undefined here. Seems like value is never changed.
I have not getting the value on alert but console is printing the value. I need help if possible for two issues.
How can I change the value from inside success and return to controller?
Is there any way that I don't have to inject $Http from controller to function?
-----It would be nice if I can do that for unit testing.
you would ideally want to pull the $http service out of the controller and make a factory to do those calls.
in the factory have a function that accepts the data you are wanting to send and have it return the promise back to the controller
something like this
Repo
app.factory("fooRepo", ["$http", function($http){
return {
getValue: function(data){
return $http({
method: "POST",
url: "/myUrl"
});
}
};
}]);
Serivce
app.factory("foo", ["$q", "fooRepo", function($q, fooRepo){
return {
getValue: function(data){
var deferred = $q.defer();
fooRepo.getValue(data)
.success(function(results){
//do some work
deferred.resolve(results);
})
.error(function(error){
// do some work
deferred.reject(error);
});
return deferred.promise;
}
}
}]);
here is the controller
app.controller("fooCtrl", ["foo", function(foo){
foo.getValue(dataHere)
.then(function(results){
// do something here
});
}]);
Added Plunkr
As you are calling a method which is doing async call, you must return promise from there as you don't know when data will get back from the ajax. On success of that ajax you should update your variable. Outside of ajax function you will get undefined value because value is returned in async manner.
Function
function getvalue(Data) {
var value=undefined;
return $http({
url: /myurl,
method: "GET",
params: {
tmp: Data.tmp,
pressure: Data.pressure
}
})
}
Controller
getvalue(formData).success(function (data, status, headers, config) {
console.log(data);//you will get data here
});
A $http-Request is async. That means that alert will be called before the .success(..) callback is executed.
You can see a result on the console because it reflects changes made after the call of console.log().
Calling alert(value); in the .success()-Callback will shield the desired result.
Replace:
}).success(function (data, status, headers, config) {
value=parseFloat( console.log(data));//console working here
return value;
});
with:
}).success(function (data, status, headers, config) {
alert(parseFloat(data));
});
I want to call a particular function: GetSession() at the beginning of my application load. This function makes a $http call and get a session token: GlobalSessionToken from the server. This session token is then used in other controllers logic and fetch data from the server. I have call this GetSession()in main controller: MasterController in $routeChangeStart event but as its an asynchronous call, my code moves ahead to CustomerController before the $http response.
Here is my code:
var GlobalSessionToken = ''; //will get from server later
//Define an angular module for our app
var myApp = angular.module('myApp', ['ngRoute']);
//Define Routing for app
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/customer', {
templateUrl: 'partials/customer.html',
controller: 'CustomerController',
resolve: {
loadData: function($q){
return LoadData2($q,'home');
}
}
}).
otherwise({
redirectTo: '/home'
});
}]);
//controllers start here and are defined in their each JS file
var controllers = {};
//only master controller is defined in app.js, rest are in separate js files
controllers.MasterController = function($rootScope, $http){
$rootScope.$on('$routeChangeStart', function(){
if(GlobalSessionToken == ''){
GetSession();
}
console.log('START');
$rootScope.loadingView = true;
});
$rootScope.$on('$routeChangeError', function(){
console.log('ERROR');
$rootScope.loadingView = false;
});
};
controllers.CustomerController = function ($scope) {
if(GlobalSessionToken != ''){
//do something
}
}
//adding the controllers to myApp angularjs app
myApp.controller(controllers);
//controllers end here
function GetSession(){
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
}).error(function (data, status, headers, config) {
console.log(data);
});
}
And my HTML has following sections:
<body ng-app="myApp" ng-controller="MasterController">
<!--Placeholder for views-->
<div ng-view="">
</div>
</body>
How can I make sure this GetSession() is always called at the very beginning of my application start and before any other controller calls and also called only once.
EDIT: This is how I added run method as per Maxim's answer. Still need to figure out a way to wait till $http call returns before going ahead with controllers.
//Some initializing code before Angular invokes controllers
myApp.run(['$rootScope','$http', '$q', function($rootScope, $http, $q) {
return GetSession($http, $q);
}]);
function GetSession($http, $q){
var defer = $q.defer();
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
defer.resolve('done');
}).error(function (data, status, headers, config) {
console.log(data);
defer.reject();
});
return defer.promise;
}
Even though some of the solutions here are perfectly valid, resolve property of the routes definition is the way to go, in my opinion. Writing your app logic inside session.then in every controller is a bit too much , we're used such approach too in one of the projects and I didn't work so well.
The most effective way is to delay controller's instantiation with resolve, as it's a built-in solution. The only problem is that you have to add resolve property with similar code for every route definition, which leads to code duplication.
To solve this problem, you can modify your route definition objects in a helper function like this:
function withSession(routeConfig) {
routeConfig.resolve = routeConfig.resolve || {};
routeConfig.resolve.session = ['getSessionPromise', function(getSessionPromise) {
return getSessionPromise();
}]
return routeConfig;
}
And then, where define your routes like this:
$routeProvider.when('/example', withSession({
templateUrl: 'views/example.html',
controller: 'ExampleCtrl'
}));
This is one of the many solutions I've tried and liked the most since it's clean and DRY.
You can't postpone the initialisation of controllers.
You may put your controller code inside a Session promise callback:
myApp.factory( 'session', function GetSession($http, $q){
var defer = $q.defer();
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
defer.resolve('done');
}).error(function (data, status, headers, config) {
console.log(data);
defer.reject();
});
return defer.promise;
} );
myApp.controller( 'ctrl', function($scope,session) {
session.then( function() {
//$scope.whatever ...
} );
} );
Alternative: If you don't want to use such callbacks, you could have your session request synchronous, but that would be a terrible thing to do.
You have not provided any details related to GetSession. For scenarios like this you should use the resolve property while defining your routes in $routeProvider. I see you are using resolve already.
What you can do now is to wrap the GlobalSessionToken into a Angular service like GlobalSessionTokenServiceand call it in the resolve to get the token before the route loads. Like
resolve: {
loadData: function($q){
return LoadData2($q,'home');
},
GlobalSessionToken: function(GlobalSessionTokenService) {
return GlobalSessionTokenService.getToken() //This should return promise
}
}
This can then be injected in your controller with
controllers.MasterController = function($rootScope, $http,GlobalSessionToken){
I just want to send the following JSONobjects to my API backend:
{
"username":"alex",
"password":"password"
}
So I wrote the following function, using Angular $http:
$http(
{
method: 'POST',
url: '/api/user/auth/',
data: '{"username":"alex", "password":"alex"}',
})
.success(function(data, status, headers, config) {
// Do Stuff
})
.error(function(data, status, headers, config) {
// Do Stuff
});
I read in documentation for POST method that Content-Type header will be automatically set to "application/json".
But I realized that the content-type I receive on my backend (Django+Tastypie) api is "text/plain".
This cause my API to not respond properly to this request. How should I manage this content-type?
The solution I've moved forward with is to always initialize models on the $scope to an empty block {} on each controller. This guarantees that if no data is bound to that model then you will still have an empty block to pass to your $http.put or $http.post method.
myapp.controller("AccountController", function($scope) {
$scope.user = {}; // Guarantee $scope.user will be defined if nothing is bound to it
$scope.saveAccount = function() {
users.current.put($scope.user, function(response) {
$scope.success.push("Update successful!");
}, function(response) {
$scope.errors.push("An error occurred when saving!");
});
};
}
myapp.factory("users", function($http) {
return {
current: {
put: function(data, success, error) {
return $http.put("/users/current", data).then(function(response) {
success(response);
}, function(response) {
error(response);
});
}
}
};
});
Another alternative is to use the binary || operator on data when calling $http.put or $http.post to make sure a defined argument is supplied:
$http.put("/users/current", data || {}).then(/* ... */);
Try this;
$http.defaults.headers.post["Content-Type"] = "application/json";
$http.post('/api/user/auth/', data).success(function(data, status, headers, config) {
// Do Stuff
})
.error(function(data, status, headers, config) {
// Do Stuff
});