abort ui-router transition asynchronously without generating a TransitionRejection log - angularjs

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

Related

Instant redirection gives error in console

$http.post(url, data).then(function (response) {
// TypeError: Cannot read property '$destroy' of null
$state.go('some.page');
// No Error
$timeout(function () {
$state.go('some.page');
}, 500);
});
500ms delay prevent from error.
Why instant page change gives "Cannot read property '$destroy' of null" error?
My initial thought on this was wondering where the best place to use $state.go is. I have always used it in the controller and didn't even know you could do it in the service... But at the end of the day, it shouldn't matter. So I did some googling on it and found this issue on github! It seems to have the same problem that was solved with a timeout but just by moving the $state.go into the controller fixes it as well.
https://github.com/angular-ui/ui-router/issues/403
Hope it this helps!
Edit: Here is another stackoverflow question about something similar
AngularJS behaving strangely when there's a nested relationship of promises in services

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

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

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

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.

Resources