Implementing page loader in Angular using promise - angularjs

I am wondering what would be the best way to implement a function, that would be responsible for toggling a gif on certain requests. I don't want to display it on all of the http requests, so I don't want to place it within the interceptor - I want to create a service.
I was most likely going to go with a function that will work as following:
LoaderFunction(SomeFunction())
Which will toggle a img or whatever whenever the promise is initiated and resolved. The SomeFunction() will usually be http requests, which already have their own promise.
Thanks!

The easiest way would be to use a counting semaphore and the disposer pattern. It would work for any number of ongoing requests.
withLoader.count = 0;
function withLoader(fn){
if(count === 0) initiateLoader();
count++;
return $q.when(fn).finally(function(){
count--;
if(count === 0) finishLoader();
});
}
This binds a scope to the lifetime of an event - like RAII in C++. This'd let you do:
withLoader(someFunction);
withLoader(someFunction);
withLoader(function(){
return $http.post(...);
}).then(function(){
// http request finished, like your normal `then` callback
});
The loader would finish when the counter reaches 0, that is when all requests finish.
Since it's Angular, you probably want to put withLoader in its own service and have a directive manage the loading itself.

Well, the easiest way to do it is through the use of promises on the $http calls themselves.
Example:
$http.post("/", data, function() {
initiateLoader(); // this is a function that starts the loading animation.
}).success(function (data) {
finishLoader(); // this is a function that ends the loading animation.
}).catch(function(err) {
finishLoader(); //so the loader also finishes on errors.
});

Related

How to prevent browser window from closing in AngularJS until the promise is resolved

I have the following code:
$window.onbeforeunload = function (event) {
event.preventDefault();
// some asynchronous code
};
I want the onbeforeunload event to wait until the asynchronous code is executed. Not sure if this can be achieved across all browsers supporting AngularJS.
Regardless of whether you are using AngularJS, or any other framework, delaying window unload until an async action completes is not something that modern browsers support anymore, as it creates a bad user experience.
However, assuming that the async thing you want to do is make an API request, then the modern solution is to use the navigator.sendBeacon() method instead. It is guaranteed to be sent in a non-blocking fashion by the browser, even after the window has been unloaded. It is a nicer solution for everyone.
Note that beacon requests have to be sent as POST requests, so your API needs to support this method for any endpoints you wish to use in an unload handler.
One can create a handler that looks at a spinner flag:
var spinnerFlag = false;
$window.addEventListener('beforeunload',unloadHandler);
$scope.$on("$destroy", function() {
$window.removeEventListener('beforeunload',unloadHandler);
});
function unLoadHandler(e) {
if (spinnerFlag) {
// Cancel the event
e.preventDefault();
// Chrome requires returnValue to be set
e.returnValue = '';
};
});
Then one can set and clear the flag:
spinnerFlag = true;
var promise = promiseBasedAPI(arguments).finally(function() {spinnerFlag = false;});
This will set the flag before starting the asynchronous operation and clear the flag when the promise either succeeds or rejects.
The user will be prompted to confirm the page unload if the promise has not resolved.
For more information, see
MDN Web API Reference - onbeforeunload
Prevent a webpage from navigating away using JavaScript — this answer
How to show the "Are you sure you want to navigate away from this page?" when changes committed?
Can beforeunload/unload be used to send XmlHttpRequests reliably

Always do action before and after $http

I want my app to always do some actions before and after $http running like loading animation or delete all messages.
// Add loading animation + Remove all messages
$http({}).then(function(response) {
// Remove loading animation + Add Success messages
}, function(response) {
// Remove loading animation + Add Failure mesages
});
But if I code like this, my code is not DRY.
How can I do this without repeat myself?
Thank you!
You should be able to implement it with Interceptors. Read the below one.
http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/
You can inject your custom service into the Interceptor component. In the request function enable animation ON and turn it OFF in the response function. Your home page should also use the same service to have an animated DIV with an ng-if like
<div ng-if="vm.myHttpStateService.isBusy">
..... animation here ...
</div>
Use the promise's .finally method
//Add loading animation + Remove all messages
$http({}).finally(function() {
//Remove loading animation
}).then(function onSuccess(response){
//Add Success messages
}).catch(function onReject(response){
//Add Failure mesages
});
From the Docs:
The Promise API
Methods
finally(callback, notifyCallback) – allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful to release resources or do some clean-up that needs to be done whether the promise was rejected or resolved. See the full specification for more information.
-- AngularJS $q Service API Reference -- The Promise API

Angularjs - Can i use a controller to track when all http requests it makes are completed

I have web page interface that has three main blocks. Each block has controllers that make http requests. I want to track the calls for each block so i can unblock the interface for each block when all its calls are completed. I have a $http.interceptor that keeps count of ALL http requests and responses for the page, and can block the page until all requests have been completed, but the client doesnt like this approach.
Any suggestions.
Thanks ahead of time.
Yes, just inject and use angular's $q service and $q.all(promises), which according to the documentation
combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
You can use code that looks something like this
var promiseA = $http.(......);
var promiseB = $http.(......);
var promiseC = $http.(......);
$q.all([promiseA, promiseB, promiseC]).then(function() {
//do whatever and unblock
});
one very simple way to do achieve this would be to set a "block" model on each of those individual controllers until the success of your http request. That eliminates the need for any additional controllers. Example code for inside of one of those controllers:
js
$scope.block1Loading = true;
$http
.get(url)
.success(function() {
$scope.block1Loading = false;
});
html
<div ng-controller="Block1Controller"
ng-class="{'blocked': block1Loading}">block 1</div>

AngularJS $http use data into controller

I'm new in AngularJS so if the question is not 'intelligent' for you, please don't rate it in negative. If someone ask a question, for he isn't stupid.
So..
I would like to use data from an ajax request, like this:
encryptApp.factory('getData', function($http, $rootScope) {
var getData = {};
getData.tot_of = function() {
return $http.get('/path/to').then(function(result) {
return result.data;
});
}
getData.get_info = function() {
return $http.get('/path/to').then(function(result) {
return result.data;
});
}
return getData;
});
In controller I use this:
getData.get_info().then(function(get_info) {
$scope.get_info = get_info;
});
// HERE THE $scope.get_info is UNDEFINED
I'm new in AngularJS and I don't know why does this. So, is there a method that I can use the json data outside the " then function ".
Thanks and please don't rate this question negative. Sorry if my english is not good.
$http.get returns a promise.
By essence, a promise is as Javascript saying:
"Hey ! I let you make the request, but please, I don't want to wait for you, so when you finished, please execute the callback I'm just passing you, since now, I will forget you since I have more code to execute while you're doing your job".
In other words, a promise's callback isn't executed immediately, since the goal is to not block the Javascript "thread" (Javascript is like single-threaded).
So your current code is acting like this:
getData.get_info().then(function(get_info) { //the function inside this "then" IS the callback
$scope.get_info = get_info;
});
// Hey !! The request might not finish ! So don't expect $scope to have the value you expect here !
So the simple example to illustrate would be to imagine that your ajax request takes 100ms to execute.
Within those 100ms, your next Javascript scope is very very very likely to be already reached, having $scope.get_info not initialized yet.
Without promise, your next code, outside of the callback, that should not depend of $scope.get_info, would have to wait 100ms to start, wasting time.
So, is there a method that I can use the json data outside the " then
function ".
There is a way, using broadcasting/emit ($rootScope.$broadcast/$rootScope.$emit) to trigger a corresponding event, but it's often more "anti-KISS" for a simple case.
I advise you to put all your depending code in the promise callback.
To clean your code, merely call a private function that you define outside the callback.

angularjs http.get only works first time

I have a function in my controller that calls an api to retrieve some values:
$scope.Refresh= function(){
$http.get('/get/value')
.success(function(data) {
//some actions
})
.error(function(data) {
//some actions
});
} ;
I want to refresh the values occasionally, so I've done:
setInterval($scope.Refresh, 100000);
I will do in a better way, but now I want to solve this.
but there is a problem:
If, in the controller, I say: $scope.Refresh (to execute the function first time), the controller does nothing.
If I write the same function + setInterval (to test and run it) it works first time (outside the function), but never refresh next times (code function inside), to explain, that execute the function but neither .success nor .error is called.
I have seen the headers with a 304 status (not modified) but the values are modified!!
I tried to disable cache but that did not fix the problem.
I tried to give a random value to the route like: /get/value/(randomNuber) but I get nothing
Where is the problem?
Just running:
$scope.Refresh();
should definitely run the function at least once. If it doesn't something is wrong with your code or with your server route. But you should be getting a console error if that's the case.
For setInterval, you should be using the $interval service that ensures your code is run within the angular loop.
Also, per the documentation, you should explicitly cancel this interval when your controller is destroyed.
var httpInterval = $interval($scope.Refresh, 100000);
$scope.$on('$destroy', function() {
$interval.cancel(httpInterval);
});
I've only had intermittent luck with .success and .error, and I'd like to think that part of it was caching the request. I have very consistent, successful results using .then, as shown:
$scope.Refresh= function(){
var myGet = $http.get('/get/value');
myGet.then(function(data){
//do success things here
}, function(data){
//do error things here
});
};
Other than that, follow the advice that #theJoeBiz gave regarding $interval and you should be fine.

Resources