AngualrJS - How to manage usability with multiple http error messages? - angularjs

I am writing an Angular 1.5.3 app. On my main 'home' page controller I call 2 different endpoints with $http. The two endpoints are not related to each other and have separate data etc. One problem I am having is if both of those endpoints have an error then my user is going to see 2 error messages. The messages may or may not be the same (e.g. sometimes no internet connection).
FirstService.request()
.then(handleFSSuccess)
.catch(handleError);
SecondService.request()
.then(handleSSSuccess)
.catch(handleError)
function handleError(error) {
ErrorService.showErrorBanner(error);
}
I cannot use the $q.all feature or promise chaining because even if the first promise fails I want the second to continue.
I am not sure how I can manage to run all promises regardless if they one or all fail, and only show one error banner. If I only show one error banner, then the other error messages may not be shown (prevent more than one error banner from being shown).
It's better for usability if only one is shown.

FirstService.request() and SecondService.request() both return Promise and you resolve it with then/catch.
I would wrap it with new Promise that will resolve them even if failure will happen. For example:
var deferred = $q.defer();
FirstService.request().then(function(_response){
deferred.resolve(_response.data);
}).catch(function(error){
deferred.resolve({error:error}); // here we do not reject but pass error to resolve
});
return deferred.promise;
So now you can use $q.all and you will get only resolving data, or object or error object.
In one place you can filter data by error key (because you get [res1, res2])
Hope it will help you

Related

abort ui-router transition asynchronously without generating a TransitionRejection log

I try to abort a ui-router transition without having an error log entry, i cannot use transition.abort() because i have to wait for a user input ("unsaved changes. continue?") so i return a promise.
$transitions.onExit({exiting: 'main.settings'}, function(transition) {
var deferred = $q.defer();
// promise testing
$timeout(function(){
// here i need to create a Rejection Object with type = RejectType.ABORTED
deferred.reject(false);
}, 500);
return deferred.promise;
});
If i reject the promise, i get the error log entry, because i don't know, how to create a Rejection with a RejectType.ABORTED in my controller. How can i get access to the Rejection Class?
Or is there any other way, how i can abort a transition asynchronously without creating a error log entry?
Using Angular 1.5.1 and ui-router 1.0.5.
Also asked at https://github.com/ui-router/core/issues/35.
Thanks
Returning rejected promise or false value in transition hook seems like the most natural way to abort the transition.
However if you don't want to clutter the log with the error messages you need to provide defaultErrorHandler() as described here - https://ui-router.github.io/ng1/docs/latest/classes/state.stateservice.html#defaulterrorhandler
If you would like to process some specific transitions errors you need to
provide onError hook for this as described here - https://ui-router.github.io/ng1/docs/latest/classes/transition.transition-1.html#onerror
I solved this issue by defining my own defaultErrorHandler(). Now i can prevent error messages to show up after aborting a transition.
Thanks # Pavel Bely

Angular and Meteor flicker on page load

I had an issue with and angular-meteor project flickering every time a state using the campaigns subscription, would load. By flickering, I mean the data was there, then it would go away and come back a half second later.
I added this to the resolve property of the state (using ui-router):
campaigns: ($q) => {
var deferred = $q.defer();
Meteor.subscribe('campaigns', {
onReady: deferred.resolve,
onStop: deferred.reject
});
return deferred.promise;
}
The flickering stopped, but I don't really understand this code. Can someone who understand angular break this resolve/defer situation down?
Just not sure why it worked. thanks.
$q is angular's implementation of promises.
in a very itty bitty nutshell, a promise has two callbacks that resolve when data is returned; a resolve function if the call succeeds and a reject if the call fails. whatever data it gets will be passed into these functions (essentially doing deferred.resolve(data) or deferred.reject(error)) . $q.defer() allows us to assign the resolution/rejections later.
meteor's subscribe function takes a few arguments. the string name of the collection, a function that returns an array of arguments to be passed to the collection, and an object/function. the object part of the final argument expects an "onReady" and "onStop" functions, and will execute those functions and pass along any data it gets. we pass in our callbacks here.
finally, we return the promise. resolve.campaigns will be a promise, which we can get the values from using the .then(successCallback, failureCallback) call. meteor handles this behind the scenes.

In Angular, what's the conceptual difference between the error and catch functions for promises?

I finally got Angular promise error handling down but it was counter-intuitive to me. I expected errors to be handled by the failure callback but instead I had to use a catch.
I don't quite understand conceptually why the catch is executed rather than the failure callback.
What I expected:
SomeAsyncService.getData().then(function (result) {
// The call is successful.
// Code in this block throws an error.
}, function (error) {
// I expected to handle errors here.
});
What eventually worked.
SomeAsyncService.getData().then(function (result) {
// The call is successful.
// Code in this block throws an error.
}).catch(function (error) {
// Where the error is actually caught.
});
If there is a more appropriate way to handle promise errors, let me know.
The second argument should be almost never be used literally in application code while also using the first. It is mostly about promise library interoperability between different implementations.
You should always use .catch unless you specifically have some weird corner case where you need .then(succcess, fail).
See The .then(success, fail) anti-pattern.
Also Q library (The one angular $q is based on) has similar section in their readme
I think you're slightly misunderstanding how promises work.
In your first code block there is only one promise object and it's SomeAsyncService.getData(). The errorCallback is not called here because that promise is resolved.
In the second code block there are actually 2 promise objects you're working with. Note that .then() "returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback". So what's happening is you're catching the error from the second promise returned from SomeAsyncService.getData().then(...).
By angularJS documentation for $q:
Methods
then(successCallback, errorCallback, notifyCallback) – regardless of
when the promise was or will be resolved or rejected, then calls one
of the success or error callbacks asynchronously as soon as the result
is available.
.....
catch(errorCallback) – shorthand for promise.then(null, errorCallback)
The two pieces of code you posted are identical.

How to intercept AngularJS $http logging for display in page

I want to intercept console log message from AngularJS and display them in a div on the page. I need this in order to debug ajax traffic in a PhoneGap app.
This is an example of the kind of errors I want to capture:
I tried this Showing console errors and alerts in a div inside the page but that does not intercept Angular error messages.
I also tried the solution gameover suggested in the answers. No luck with that either. Apparently $http is handling error logging differently.
I guess the answer you tried has the right idea but you're overriding the wrong methods. Reading here I can see angularJs uses $log instead of console.log, so to intercept you can try to override those.
Something like this:
$scope.$log = {
error: function(msg){document.getElementById("logger").innerHTML(msg)},
info: function(msg){document.getElementById("logger").innerHTML(msg)},
log: function(msg){document.getElementById("logger").innerHTML(msg)},
warn: function(msg){document.getElementById("logger").innerHTML(msg)}
}
Make sure to run that after importing angular.js.
EDIT
Second guess, override the consoleLog method on the LogProvider inner class on angular.js file:
function consoleLog(type) {
var output ="";
//arguments array, you'll need to change this accordingly if you want to
//log arrays, objects etc
forEach(arguments, function(arg) {
output+= arg +" ";
});
document.getElementById("logger").innerHTML(output);
}
I've used log4javascript for this purpose. I create the log object via
var log = log4javascript.getLogger('myApp')
log.addAppender(new log4javascript.InPageAppender());
I then use this in a value dependency, and hook into it where needed (e.g. http interceptor).
A more lightweight approach might be to use $rootScope.emit and then have a component on your main page which prepends these log messages to a visible div, but this will require you to change all your calls to console.log (or redefine the function in your js).
I think that this message is not even displayed from AngularJS. It looks like an exception which has not been caught in any JavaScript (angular.js just appears on top of your stack because that's the actual location where the HTTP request is being sent).
Take a look at ng.$exceptionHandler. That should be the code you seem to be interested in. If not, take a quick web search for „JavaScript onerror“ which should tell you how to watch for these kinds of errors.
I would rather user an $http interceptor.
Inside the responseError function, you can set a property on a service that will be exposed to the div's scope.

can we resolve any deferred with any response in angularjs

I cant understand, certain things in this code http://www.espeo.pl/2012/02/26/authentication-in-angularjs-application
This code, this trying to resend same request(the one user requests before logging in) after the user logs in.
My question
Inside the retry function, they are resloving a deffered with the
response of a request that is sent after the user logs in. The
response and the deffered are no way related to each other. Why they
should do this?
-
function retry(req) {
$http(req.config).then(function(response) {
req.deferred.resolve(response);
});
}
Please have a look at the code in the above url for understanding how the retry method works.
You can resolve a promise with whatever you want. In this case, a promise was returned to the application when the 401 status code/error was first encountered. Now, after the loginConfirmed event, the request is sent again and the response is used to resolve that promise.

Resources