@@ -169,6 +169,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
169169 }
170170 } ] ) ;
171171 }
172+
172173 return state ;
173174 }
174175
@@ -220,6 +221,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
220221
221222 var TransitionSuperseded = $q . reject ( new Error ( 'transition superseded' ) ) ;
222223 var TransitionPrevented = $q . reject ( new Error ( 'transition prevented' ) ) ;
224+ var TransitionAborted = $q . reject ( new Error ( 'transition aborted' ) ) ;
223225
224226 root . locals = { resolve : null , globals : { $stateParams : { } } } ;
225227 $state = {
@@ -238,18 +240,33 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
238240 toParams = toParams || { } ;
239241 options = extend ( { location : true , inherit : false , relative : null } , options ) ;
240242
243+ var from = $state . $current , fromParams = $state . params , fromPath = from . path ;
244+
241245 var toState = findState ( to , options . relative ) ;
242246
247+ var evt ;
248+
243249 if ( ! isDefined ( toState ) ) {
244- if ( options . relative ) throw new Error ( "Could not resolve '" + to + "' from state '" + options . relative + "'" ) ;
245- throw new Error ( "No such state '" + to + "'" ) ;
250+ // Broadcast not found event and abort the transition if prevented
251+ var redirect = { to : to , toParams : toParams , options : options } ;
252+ evt = $rootScope . $broadcast ( '$stateNotFound' , redirect , from . self , fromParams ) ;
253+ if ( evt . defaultPrevented ) return TransitionAborted ;
254+ // Always retry once if the $stateNotFound was not prevented
255+ // (handles either redirect changed or state lazy-definition)
256+ to = redirect . to ;
257+ toParams = redirect . toParams ;
258+ options = redirect . options ;
259+ toState = findState ( to , options . relative ) ;
260+ if ( ! isDefined ( toState ) ) {
261+ if ( options . relative ) throw new Error ( "Could not resolve '" + to + "' from state '" + options . relative + "'" ) ;
262+ throw new Error ( "No such state '" + to + "'" ) ;
263+ }
246264 }
247265 if ( toState [ 'abstract' ] ) throw new Error ( "Cannot transition to abstract state '" + to + "'" ) ;
248266 if ( options . inherit ) toParams = inheritParams ( $stateParams , toParams || { } , $state . $current , toState ) ;
249267 to = toState ;
250268
251- var toPath = to . path ,
252- from = $state . $current , fromParams = $state . params , fromPath = from . path ;
269+ var toPath = to . path ;
253270
254271 // Starting from the root of the path, keep all levels that haven't changed
255272 var keep , state , locals = root . locals , toLocals = [ ] ;
@@ -272,7 +289,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
272289 toParams = normalize ( to . params , toParams || { } ) ;
273290
274291 // Broadcast start event and cancel the transition if requested
275- var evt = $rootScope . $broadcast ( '$stateChangeStart' , to . self , toParams , from . self , fromParams ) ;
292+ evt = $rootScope . $broadcast ( '$stateChangeStart' , to . self , toParams , from . self , fromParams ) ;
276293 if ( evt . defaultPrevented ) return TransitionPrevented ;
277294
278295 // Resolve locals for the remaining states, but don't update any global state just
0 commit comments