Skip to content

Instantly share code, notes, and snippets.

@deanapeterson
Last active November 25, 2016 07:33
Show Gist options
  • Save deanapeterson/b93b48fd8c258861f26b to your computer and use it in GitHub Desktop.
Save deanapeterson/b93b48fd8c258861f26b to your computer and use it in GitHub Desktop.
Adding "$stateChangeRejected" event to ui-router.
(function(){
"use strict";
/**
* Hijacks the ui-router $state.transitionTo() method to capture it's promise.
* the promise is added to the $state as $promise (may or may not be needed);
* also adds handler for rejection of the $promise.
*/
angular
.module("stm.ui.stateChangeRejected", ['ui.router'])
.config(stateChangeRejected);
function stateChangeRejected($provide){
$provide.decorator("$state", decorateTransitionTo);
decorateTransitionTo.$inject = ['$delegate', '$rootScope'];
function decorateTransitionTo($delegate, $rootScope){ //$delegate === $state
var nativeTransitionTo = $delegate.transitionTo; //transfer reference
$delegate.transitionTo = transitionToWrapper;// replace with wrapper
return $delegate;
function transitionToWrapper(){
var args = [].slice.call(arguments);
var promise = nativeTransitionTo.apply(this, args);//call original transitionTo, capture promise
promise['catch'](onStateRejection); //add handler for rejection
$delegate.$promise = promise; //add $promise to default $state object
return promise;
function onStateRejection(error){
var toState = $delegate.get(args[0]);
var toParams = args[1];
$rootScope.$broadcast("$stateChangeRejected", toState, toParams, $delegate.current, $delegate.params, error);
return error;
}
}
}
}
}());
@sabrehagen
Copy link

how does onStateRejection get thrown? is this the responsibility of the user?

@bogdanalexe90
Copy link

I think that for a lot of reasons it will be better to return the promise generated by this guy:
promise['catch'](onStateRejection);

Also you can throw / return rejection with the error after the broadcast (from onStateRejection).

** Just an idea:

var promise = nativeTransitionTo.apply(this, args);//call original transitionTo, capture promise

promise = promise['catch'](onStateRejection);   //add handler for rejection
$delegate.$promise = promise;       //add $promise to default $state object

return promise;

function onStateRejection(error){
    var toState = $delegate.get(args[0]);
    var toParams = args[1];

    $rootScope.$broadcast("$stateChangeRejected", toState, toParams, $delegate.current, $delegate.params, error);
    return $q.reject(error);
}

@deanapeterson
Copy link
Author

@sabrehagen, rejections are when a event.preventDefault() is executed inside the "$stateChangeStart/Success" handlers. Also if a dependency doesn't resolve and returns a rejected promise which is changed to transitionTo promise.

@bogdanalexe90, UPDATED, thanks for reminding me. Pretty sure I don't need to use $q.reject as returned value is converted into a rejected promise automatically. Thanks

@bogdanalexe90
Copy link

If you use return then you will recover from error and you will end up in success callback for the promise returned by the .then() method. You must use $q.reject() (or throw an error) in order to reject the returned promise. This explains better (look at the example) https://docs.angularjs.org/api/ng/service/$q#reject

Also you must return the promise generated from here:
promise['catch'](onStateRejection);

  • In order to be sure that this will happen sequentially (first the broadcast and then the final handle of the promise).

@deanapeterson
Copy link
Author

@bogdanalexe90, I'm gonna take a look at that. Thanks

@mr-White
Copy link

mr-White commented Nov 9, 2016

Thank you @deanapeterson ! It's an informative gist that works on v0.2.18 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment