I am trying to retrofit spinners into my app.
I'm expecting to set a loading=true variable when I start async events, and set it false when the call returns.
Then in my view I can do
<span><i class="fa fa-spinner" if-show="vm.loading"></i><span>
I was hoping to find async calls of the form success, failure, finally.
The first controller I opened up makes a call in a form I don't understand. I don't even know what to call it, so I have no idea how to research and explore it.
$scope.login = function () {
if ($scope.form.$valid) {
authService.login($scope.loginData).then(function (response) {
$location.path("/dashboard");
},
function (err) {
toastr.error(err.error_description);
});
}
};
What I see here is an if statement, followed by a comma, followed by a function.
Uhh... is that some form of try/catch I've not encountered before?
I can't just add a finally on the end...
The reason I'm asking the question here is because I don't even know how to research this.
Ultimately the question I'm trying to answer is: what form of async call can I use so that I have a place to first activate the spinner, and then deactivate it?
Ah. OK. It's a standard promise - just confusingly formatted. I overlooked the .then that's on the same line.
$scope.login = function () {
if ($scope.form.$valid) {
$scope.loading = true;
authService.login($scope.loginData)
.then(function (response) {
$location.path("/dashboard");
},
function (err) {
toastr.error(err.error_description);
})
.finally(function(){
$scope.loading = false;
}
);
}
}
Found it here:
How to always run some code when a promise is fulfilled in Angular.js
Related
I have an error in line callback(response.data) telling that callback is not a function. Well, I am new to this and I am trying to figure out how to fix this issue but I am having a tough time. Any idea is appreciated. Thanks
app.service('NotificationPollService', function ($q, $http, $timeout) {
var notification = {};
notification.poller = function (callback, error) {
return $http.get('api/sample.php').then(function (response) {
if (typeof response.data === 'object') {
callback(response.data);
} else {
error(response.data);
}
$timeout(notification.poller, 2000);
});
}
notification.poller();
return notification;
});
You declared poller as a function that receives two functions as its parameters, but you are invoking it with no parameters in two different places:
notification.poller();
and
$timeout(notification.poller, 2000);
Remember you can log the value of the variables to the console as console.log(callback, error), in which case you will see it prints undefined, undefined.
What was the intention of invoking notification.poller() in that line? Looks like that function should be called by the user of your service instead, like this:
notification.poller(data => console.log(`Received some data! ${data}`),
error => console.error(`Some error occurred: ${error}`));
Lastly, what is the intention of poller? By the name I imagined it was going to invoke that endpoint some fixed X number of times (polling) and after X failures it would give up, but it's always invoking poller again, even after a success.
I am trying to cancel a $transitions change under certain conditions using ui-router.
In my run block, I have the following code:
$transitions.onStart( { from: 'createCatalog.previewStyles'}, function(trans) {
var from = trans.from(),
to = trans.to();
previewStylesService.checkSave()
.then(function success() {
return $state.target(to);
}, function err() {
return $state.target(from);
});
});
My previewStylesService checkSave function looks like this:
function checkSave() {
var deferred = $q.defer()
if (dataChanged) {
if (confirm('Would you like to save the changes made to the catalog?')) {
catalogService.prepCatalogSave()
.then(function success() {
deferred.resolve();
}, function err () {
deferred.reject();
})
} else {
deferred.resolve();
}
} else {
deferred.reject();
}
return deferred.promise;
}
Then based on the above conditions, the $transition will either take place or will cancel. The problem is, even if the above code's promise is rejected, the state still changes to the originally requested state. How can I "cancel" the state change in this case?
I do know it is may be late to help you but It could help others.
I just ran into the same problem and after about a couple of hours of research/doc reading I came to the conclusion that $transitions.onBlaBla callbacks had a return value that could be true (the transition resume normaly), false (transition is canceled), or a promise (transitionService will wait for this promise rejection/resolve to decide if it needs to do the transition or not
You could try to return
return previewStylesService.checkSave()
to see what happens or try to do it differently with a return true/false and some other code hooks
Here is the link of hook result that is return by your onSuccess Callback:
https://ui-router.github.io/ng1/docs/latest/modules/transition.html#hookresult
Simply return false from your hook to cancel the transition:
https://ui-router.github.io/ng1/docs/latest/modules/transition.html#hookresult
I have a code to authentication where I want call an event after receive some data, but "$scope.$emit" not working in the callback of "User.me()" and I not understand.
Anybody can explain me?
$scope.login = function () {
OAuth.getAccessToken($scope.user).then(function () {
$scope.$emit('event:here:work');
User.me({}, {}, function (data) {
$scope.$emit('event:here:NOT-WORK');
});
$scope.$emit('event:here:work');
}, function (response) {
// error //
});
};
I try:
$scope.$emit('event'); // but, not work
$scope.$broadcast('event'); // but, not work
$rootScope.$emit('event'); // but, not work
$rootScope.$broadcast('event'); // work!!!!
Now, the "why" I not know?
Use $scope.$broadcast instead.
I need to prevent sending the same request repeatedly to API before the previous request will give the response. I have found out some solutions. But, I don't want to disable the button while waiting for response because I have more API calls in my app.
I really need to do something in my $provider .config() .I found a way here(http://blog.codebrag.com/post/57412530001/preventing-duplicated-requests-in-angularjs).
But I need more clarification code. Any kind of reference about this is welcome.
Lets say you have $http in your controller.js file.
Many request to server
$http.get('/link/to/file.php');
Just one request to server, no matter how many times you will call this method:
$http.get('/link/to/file.php', {cache: true});
Example:
(function() {
'use strict';
angular
.module('yourModuleName')
.controller('DashboardCtrl', DashboardCtrl);
DashboardCtrl.$inject = ['$scope'];
function DashboardCtrl($scope) {
$scope.get = function () {
$http.get('/link/to/file.php', {cache: true}).then(function(response) {
// it will do GET request just once
// later you will get the same response from cacheFactory
})
}
}
}());
I would like to complement #VikasChauhan answer, however I do not have enough reputation to comment on his answer.
His code works great for me, except the part where he returns null. That causes Angular to throw a bunch of errors all over.
Instead of null, I simply reject the request:
return $q.reject(request);
Here's my function:
$httpProvider.interceptors.push(['$injector', '$q', function interceptors($injector, $q) {
return {
// preventing duplicate requests
request: function request(config) {
var $http = $injector.get('$http');
var _config = angular.copy(config);
delete _config.headers;
function isConfigEqual(pendingRequestConfig) {
var _pendingRequestConfig = angular.copy(pendingRequestConfig);
delete _pendingRequestConfig.headers;
return angular.equals(_config, _pendingRequestConfig);
}
if ($http.pendingRequests.some(isConfigEqual)) {
return $q.reject(request);
}
return config;
}
};
}
]);
Hope this helps other people.
You can create a function to cancel the first http request, when calling the another one on the button click.
Here is a reference that uses $q.defer() function that helped me on a similar issue:
http://odetocode.com/blogs/scott/archive/2014/04/24/canceling-http-requests-in-angularjs.aspx
In my project, i was facing this problem. I found a very useful working solution here
And implemented it inside the config:
function config($routeProvider, $httpProvider) {
$httpProvider.interceptors.push(['$injector', function interceptors($injector) {
// Manually injecting dependencies to avoid circular dependency problem
return {
// preventing duplicate requests
'request': function request(config) {
var $http = $injector.get('$http'),
copiedConfig = angular.copy(config);
delete copiedConfig.headers;
function configsAreEqual(pendingRequestConfig) {
var copiedPendingRequestConfig = angular.copy(pendingRequestConfig);
delete copiedPendingRequestConfig.headers;
return angular.equals(copiedConfig, copiedPendingRequestConfig);
}
if ($http.pendingRequests.some(configsAreEqual)) {
debugger;
return null;
}
return config;
}
}
}
]);
}
I have a complex promise chain, with each of the success handlers in then making some more API calls and passing on the results to the next then and so on.
I've come to a situation where, based on a condition, I may choose to stop the chain.
So, in a nutshell, my code looks like;
API.callGeneric(/* some params here */)
.$promise
.then(success(res) {
if (processFurther(res)) {
return API.callGeneric(res).$promise;
} else {
return someFunction(res); // so that the stuff inside the next successhandler still happens
}
}, failHandler)
.then(success(res) {
// do some stuff with res
// do some important stuff independent of res (announce app ready, etc.)
}, failHandler)
So, there is some stuff that needs to happen in the last step irrespective of whether or not I choose to return the promise from another API call or just an object.
How can that be done?
Solved the issue with the help of #BenjaminGruenbaum.
So, basically, I needed the successHandler of the last .then to execute in any case – at least the part of it that didn't depend on the promise passed on from earlier in the chain.
What solves this is using .finally, but there's a catch. .finally executes irrespective of where you decide to reject the promise and break the chain. And in my scenario, that wasn't what I needed. In .finally, I needed to announce that my webapp was ready (through a websocket) to the server and other clients. But that would not be ideal if the first API call itself had to be rejected.
What solved it was maintaining a measure of the progress through the promise chain, so that my handler in finally was completely aware of how much progress had been made. If this was above a certain limit for my app to be declared ready, I ignored the last promise rejection.
So basically, the solution now looks like this;
var progress = 0;
API.callGeneric(/* some params */).$promise
.then(successOne(res) {
progress++;
return API.callGeneric(res).$promise;
}, handleErr)
.then(successTwo(res) {
progress++;
if (isResPositive(res)) {
return API.callGeneric(res).$promise;
} else {
var def = $q.defer();
def.reject(res);
return def.promise;
}
}, handleErr)
.then(/* similar stuff */)
/* etc */
.finally(function () {
if (progress > limit) {
// do stuff here
} else {
// failure, don't do stuff
}
});
You can use a reject promise like $q.reject(reason) as a return value for function.
.then(function () {
return $q.reject()
}).then(function () {
console.log('never happens');
}).catch(function () {
console.log('you are going here because of the reject above');
}).finally(function () {
console.log('always happens');
});
Look at the documentation for more info.