Skip to content

Commit 3d3e182

Browse files
committed
refactor($state): disentangling retry logic
- Dropping unnecessary variable assignments - Misc. cleanup
1 parent f5c426b commit 3d3e182

File tree

1 file changed

+84
-68
lines changed

1 file changed

+84
-68
lines changed

src/state.js

+84-68
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,71 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
553553
}
554554
}
555555

556+
// Handles the case where a state which is the target of a transition is not found, and the user
557+
// can optionally retry or defer the transition
558+
function handleRedirect(redirect, state, params, options) {
559+
/**
560+
* @ngdoc event
561+
* @name ui.router.state.$state#$stateNotFound
562+
* @eventOf ui.router.state.$state
563+
* @eventType broadcast on root scope
564+
* @description
565+
* Fired when a requested state **cannot be found** using the provided state name during transition.
566+
* The event is broadcast allowing any handlers a single chance to deal with the error (usually by
567+
* lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
568+
* you can see its three properties in the example. You can use `event.preventDefault()` to abort the
569+
* transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
570+
*
571+
* @param {Object} event Event object.
572+
* @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
573+
* @param {State} fromState Current state object.
574+
* @param {Object} fromParams Current state params.
575+
*
576+
* @example
577+
*
578+
* <pre>
579+
* // somewhere, assume lazy.state has not been defined
580+
* $state.go("lazy.state", {a:1, b:2}, {inherit:false});
581+
*
582+
* // somewhere else
583+
* $scope.$on('$stateNotFound',
584+
* function(event, unfoundState, fromState, fromParams){
585+
* console.log(unfoundState.to); // "lazy.state"
586+
* console.log(unfoundState.toParams); // {a:1, b:2}
587+
* console.log(unfoundState.options); // {inherit:false} + default options
588+
* })
589+
* </pre>
590+
*/
591+
var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params);
592+
593+
if (evt.defaultPrevented) {
594+
syncUrl();
595+
return TransitionAborted;
596+
}
597+
598+
if (!evt.retry) {
599+
return null;
600+
}
601+
602+
// Allow the handler to return a promise to defer state lookup retry
603+
if (options.$retry) {
604+
syncUrl();
605+
return TransitionFailed;
606+
}
607+
var retryTransition = $state.transition = $q.when(evt.retry);
608+
609+
retryTransition.then(function() {
610+
if (retryTransition !== $state.transition) return TransitionSuperseded;
611+
redirect.options.$retry = true;
612+
return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
613+
}, function() {
614+
return TransitionAborted;
615+
});
616+
syncUrl();
617+
618+
return retryTransition;
619+
}
620+
556621
root.locals = { resolve: null, globals: { $stateParams: {} } };
557622
$state = {
558623
params: {},
@@ -710,63 +775,11 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
710775
var evt, toState = findState(to, options.relative);
711776

712777
if (!isDefined(toState)) {
713-
// Broadcast not found event and abort the transition if prevented
714778
var redirect = { to: to, toParams: toParams, options: options };
779+
var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
715780

716-
/**
717-
* @ngdoc event
718-
* @name ui.router.state.$state#$stateNotFound
719-
* @eventOf ui.router.state.$state
720-
* @eventType broadcast on root scope
721-
* @description
722-
* Fired when a requested state **cannot be found** using the provided state name during transition.
723-
* The event is broadcast allowing any handlers a single chance to deal with the error (usually by
724-
* lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
725-
* you can see its three properties in the example. You can use `event.preventDefault()` to abort the
726-
* transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
727-
*
728-
* @param {Object} event Event object.
729-
* @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
730-
* @param {State} fromState Current state object.
731-
* @param {Object} fromParams Current state params.
732-
*
733-
* @example
734-
*
735-
* <pre>
736-
* // somewhere, assume lazy.state has not been defined
737-
* $state.go("lazy.state", {a:1, b:2}, {inherit:false});
738-
*
739-
* // somewhere else
740-
* $scope.$on('$stateNotFound',
741-
* function(event, unfoundState, fromState, fromParams){
742-
* console.log(unfoundState.to); // "lazy.state"
743-
* console.log(unfoundState.toParams); // {a:1, b:2}
744-
* console.log(unfoundState.options); // {inherit:false} + default options
745-
* })
746-
* </pre>
747-
*/
748-
evt = $rootScope.$broadcast('$stateNotFound', redirect, from.self, fromParams);
749-
if (evt.defaultPrevented) {
750-
syncUrl();
751-
return TransitionAborted;
752-
}
753-
754-
// Allow the handler to return a promise to defer state lookup retry
755-
if (evt.retry) {
756-
if (options.$retry) {
757-
syncUrl();
758-
return TransitionFailed;
759-
}
760-
var retryTransition = $state.transition = $q.when(evt.retry);
761-
retryTransition.then(function() {
762-
if (retryTransition !== $state.transition) return TransitionSuperseded;
763-
redirect.options.$retry = true;
764-
return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
765-
}, function() {
766-
return TransitionAborted;
767-
});
768-
syncUrl();
769-
return retryTransition;
781+
if (redirectResult) {
782+
return redirectResult;
770783
}
771784

772785
// Always retry once if the $stateNotFound was not prevented
@@ -775,6 +788,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
775788
toParams = redirect.toParams;
776789
options = redirect.options;
777790
toState = findState(to, options.relative);
791+
778792
if (!isDefined(toState)) {
779793
if (options.relative) throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
780794
throw new Error("No such state '" + to + "'");
@@ -787,19 +801,22 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
787801
var toPath = to.path;
788802

789803
// Starting from the root of the path, keep all levels that haven't changed
790-
var keep, state, locals = root.locals, toLocals = [];
791-
for (keep = 0, state = toPath[keep];
792-
state && state === fromPath[keep] && equalForKeys(toParams, fromParams, state.ownParams) && !options.reload;
793-
keep++, state = toPath[keep]) {
794-
locals = toLocals[keep] = state.locals;
804+
var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
805+
806+
if (!options.reload) {
807+
while (state && state === fromPath[keep] && equalForKeys(toParams, fromParams, state.ownParams)) {
808+
locals = toLocals[keep] = state.locals;
809+
keep++;
810+
state = toPath[keep];
811+
}
795812
}
796813

797814
// If we're going to the same state and all locals are kept, we've got nothing to do.
798815
// But clear 'transition', as we still want to cancel any other pending transitions.
799816
// TODO: We may not want to bump 'transition' if we're called from a location change that we've initiated ourselves,
800817
// because we might accidentally abort a legitimate transition initiated from code?
801818
if (shouldTriggerReload(to, from, locals, options) ) {
802-
if ( to.self.reloadOnSearch !== false )
819+
if (to.self.reloadOnSearch !== false)
803820
syncUrl();
804821
$state.transition = null;
805822
return $q.when($state.current);
@@ -837,8 +854,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
837854
* })
838855
* </pre>
839856
*/
840-
evt = $rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams);
841-
if (evt.defaultPrevented) {
857+
if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
842858
syncUrl();
843859
return TransitionPrevented;
844860
}
@@ -852,9 +868,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
852868
// empty and gets filled asynchronously. We need to keep track of the promise for the
853869
// (fully resolved) current locals, and pass this down the chain.
854870
var resolved = $q.when(locals);
855-
for (var l=keep; l<toPath.length; l++, state=toPath[l]) {
871+
for (var l = keep; l < toPath.length; l++, state = toPath[l]) {
856872
locals = toLocals[l] = inherit(locals);
857-
resolved = resolveState(state, toParams, state===to, resolved, locals);
873+
resolved = resolveState(state, toParams, state === to, resolved, locals);
858874
}
859875

860876
// Once everything is resolved, we are ready to perform the actual transition
@@ -867,7 +883,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
867883
if ($state.transition !== transition) return TransitionSuperseded;
868884

869885
// Exit 'from' states not kept
870-
for (l=fromPath.length-1; l>=keep; l--) {
886+
for (l = fromPath.length - 1; l >= keep; l--) {
871887
exiting = fromPath[l];
872888
if (exiting.self.onExit) {
873889
$injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
@@ -876,7 +892,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
876892
}
877893

878894
// Enter 'to' states not kept
879-
for (l=keep; l<toPath.length; l++) {
895+
for (l = keep; l < toPath.length; l++) {
880896
entering = toPath[l];
881897
entering.locals = toLocals[l];
882898
if (entering.self.onEnter) {
@@ -1197,7 +1213,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
11971213
}
11981214

11991215
function shouldTriggerReload(to, from, locals, options) {
1200-
if ( to === from && ((locals === from.locals && !options.reload) || (to.self.reloadOnSearch === false)) ) {
1216+
if (to === from && ((locals === from.locals && !options.reload) || (to.self.reloadOnSearch === false))) {
12011217
return true;
12021218
}
12031219
}

0 commit comments

Comments
 (0)