@@ -553,6 +553,71 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
553
553
}
554
554
}
555
555
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
+
556
621
root . locals = { resolve : null , globals : { $stateParams : { } } } ;
557
622
$state = {
558
623
params : { } ,
@@ -710,63 +775,11 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
710
775
var evt , toState = findState ( to , options . relative ) ;
711
776
712
777
if ( ! isDefined ( toState ) ) {
713
- // Broadcast not found event and abort the transition if prevented
714
778
var redirect = { to : to , toParams : toParams , options : options } ;
779
+ var redirectResult = handleRedirect ( redirect , from . self , fromParams , options ) ;
715
780
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 ;
770
783
}
771
784
772
785
// Always retry once if the $stateNotFound was not prevented
@@ -775,6 +788,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
775
788
toParams = redirect . toParams ;
776
789
options = redirect . options ;
777
790
toState = findState ( to , options . relative ) ;
791
+
778
792
if ( ! isDefined ( toState ) ) {
779
793
if ( options . relative ) throw new Error ( "Could not resolve '" + to + "' from state '" + options . relative + "'" ) ;
780
794
throw new Error ( "No such state '" + to + "'" ) ;
@@ -787,19 +801,22 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
787
801
var toPath = to . path ;
788
802
789
803
// 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
+ }
795
812
}
796
813
797
814
// If we're going to the same state and all locals are kept, we've got nothing to do.
798
815
// But clear 'transition', as we still want to cancel any other pending transitions.
799
816
// TODO: We may not want to bump 'transition' if we're called from a location change that we've initiated ourselves,
800
817
// because we might accidentally abort a legitimate transition initiated from code?
801
818
if ( shouldTriggerReload ( to , from , locals , options ) ) {
802
- if ( to . self . reloadOnSearch !== false )
819
+ if ( to . self . reloadOnSearch !== false )
803
820
syncUrl ( ) ;
804
821
$state . transition = null ;
805
822
return $q . when ( $state . current ) ;
@@ -837,8 +854,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
837
854
* })
838
855
* </pre>
839
856
*/
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 ) {
842
858
syncUrl ( ) ;
843
859
return TransitionPrevented ;
844
860
}
@@ -852,9 +868,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
852
868
// empty and gets filled asynchronously. We need to keep track of the promise for the
853
869
// (fully resolved) current locals, and pass this down the chain.
854
870
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 ] ) {
856
872
locals = toLocals [ l ] = inherit ( locals ) ;
857
- resolved = resolveState ( state , toParams , state === to , resolved , locals ) ;
873
+ resolved = resolveState ( state , toParams , state === to , resolved , locals ) ;
858
874
}
859
875
860
876
// Once everything is resolved, we are ready to perform the actual transition
@@ -867,7 +883,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
867
883
if ( $state . transition !== transition ) return TransitionSuperseded ;
868
884
869
885
// Exit 'from' states not kept
870
- for ( l = fromPath . length - 1 ; l >= keep ; l -- ) {
886
+ for ( l = fromPath . length - 1 ; l >= keep ; l -- ) {
871
887
exiting = fromPath [ l ] ;
872
888
if ( exiting . self . onExit ) {
873
889
$injector . invoke ( exiting . self . onExit , exiting . self , exiting . locals . globals ) ;
@@ -876,7 +892,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
876
892
}
877
893
878
894
// Enter 'to' states not kept
879
- for ( l = keep ; l < toPath . length ; l ++ ) {
895
+ for ( l = keep ; l < toPath . length ; l ++ ) {
880
896
entering = toPath [ l ] ;
881
897
entering . locals = toLocals [ l ] ;
882
898
if ( entering . self . onEnter ) {
@@ -1197,7 +1213,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
1197
1213
}
1198
1214
1199
1215
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 ) ) ) {
1201
1217
return true ;
1202
1218
}
1203
1219
}
0 commit comments