@@ -335,9 +335,14 @@ function $ViewDirective($view: ViewService, $animate: any, $uiViewScroll: any, $
335335 return directive ;
336336} ] ;
337337
338- $ViewDirectiveFill . $inject = [ '$compile' , '$controller' , '$transitions' , '$view' , '$timeout' ] ;
338+ $ViewDirectiveFill . $inject = [ '$compile' , '$controller' , '$transitions' , '$view' , '$q' , '$ timeout'] ;
339339/** @hidden */
340- function $ViewDirectiveFill ( $compile : ICompileService , $controller : IControllerService , $transitions : TransitionService , $view : ViewService , $timeout : ITimeoutService ) {
340+ function $ViewDirectiveFill ( $compile : angular . ICompileService ,
341+ $controller : angular . IControllerService ,
342+ $transitions : TransitionService ,
343+ $view : ViewService ,
344+ $q : angular . IQService ,
345+ $timeout : ITimeoutService ) {
341346 const getControllerAs = parse ( 'viewDecl.controllerAs' ) ;
342347 const getResolveAs = parse ( 'viewDecl.resolveAs' ) ;
343348
@@ -384,7 +389,7 @@ function $ViewDirectiveFill ($compile: ICompileService, $controller: IController
384389 $element . data ( '$ngControllerController' , controllerInstance ) ;
385390 $element . children ( ) . data ( '$ngControllerController' , controllerInstance ) ;
386391
387- registerControllerCallbacks ( $transitions , controllerInstance , scope , cfg ) ;
392+ registerControllerCallbacks ( $q , $ transitions, controllerInstance , scope , cfg ) ;
388393 }
389394
390395 // Wait for the component to appear in the DOM
@@ -402,7 +407,7 @@ function $ViewDirectiveFill ($compile: ICompileService, $controller: IController
402407
403408 let deregisterWatch = scope . $watch ( getComponentController , function ( ctrlInstance ) {
404409 if ( ! ctrlInstance ) return ;
405- registerControllerCallbacks ( $transitions , ctrlInstance , scope , cfg ) ;
410+ registerControllerCallbacks ( $q , $ transitions, ctrlInstance , scope , cfg ) ;
406411 deregisterWatch ( ) ;
407412 } ) ;
408413 }
@@ -415,15 +420,23 @@ function $ViewDirectiveFill ($compile: ICompileService, $controller: IController
415420
416421/** @hidden */
417422let hasComponentImpl = typeof ( angular as any ) . module ( 'ui.router' ) [ 'component' ] === 'function' ;
423+ /** @hidden incrementing id */
424+ let _uiCanExitId = 0 ;
418425
419426/** @hidden TODO: move these callbacks to $view and/or `/hooks/components.ts` or something */
420- function registerControllerCallbacks ( $transitions : TransitionService , controllerInstance : Ng1Controller , $scope : IScope , cfg : Ng1ViewConfig ) {
427+ function registerControllerCallbacks ( $q : angular . IQService ,
428+ $transitions : TransitionService ,
429+ controllerInstance : Ng1Controller ,
430+ $scope : IScope ,
431+ cfg : Ng1ViewConfig ) {
421432 // Call $onInit() ASAP
422- if ( isFunction ( controllerInstance . $onInit ) && ! ( cfg . viewDecl . component && hasComponentImpl ) ) controllerInstance . $onInit ( ) ;
433+ if ( isFunction ( controllerInstance . $onInit ) && ! ( cfg . viewDecl . component && hasComponentImpl ) ) {
434+ controllerInstance . $onInit ( ) ;
435+ }
423436
424437 let viewState : Ng1StateDeclaration = tail ( cfg . path ) . state . self ;
425438
426- var hookOptions : HookRegOptions = { bind : controllerInstance } ;
439+ let hookOptions : HookRegOptions = { bind : controllerInstance } ;
427440 // Add component-level hook for onParamsChange
428441 if ( isFunction ( controllerInstance . uiOnParamsChanged ) ) {
429442 let resolveContext : ResolveContext = new ResolveContext ( cfg . path ) ;
@@ -450,7 +463,7 @@ function registerControllerCallbacks($transitions: TransitionService, controller
450463 if ( changedToParams . length ) {
451464 let changedKeys : string [ ] = changedToParams . map ( x => x . id ) ;
452465 // Filter the params to only changed/new to params. `$transition$.params()` may be used to get all params.
453- var newValues = filter ( toParams , ( val , key ) => changedKeys . indexOf ( key ) !== - 1 ) ;
466+ let newValues = filter ( toParams , ( val , key ) => changedKeys . indexOf ( key ) !== - 1 ) ;
454467 controllerInstance . uiOnParamsChanged ( newValues , $transition$ ) ;
455468 }
456469 } ;
@@ -459,8 +472,25 @@ function registerControllerCallbacks($transitions: TransitionService, controller
459472
460473 // Add component-level hook for uiCanExit
461474 if ( isFunction ( controllerInstance . uiCanExit ) ) {
462- var criteria = { exiting : viewState . name } ;
463- $scope . $on ( '$destroy' , < any > $transitions . onBefore ( criteria , controllerInstance . uiCanExit , hookOptions ) ) ;
475+ let id = _uiCanExitId ++ ;
476+ let cacheProp = '_uiCanExitIds' ;
477+
478+ // Returns true if a redirect transition already answered truthy
479+ const prevTruthyAnswer = ( trans : Transition ) =>
480+ ! ! trans && ( trans [ cacheProp ] && trans [ cacheProp ] [ id ] === true || prevTruthyAnswer ( trans . redirectedFrom ( ) ) ) ;
481+
482+ // If a user answered yes, but the transition was later redirected, don't also ask for the new redirect transition
483+ const wrappedHook = ( trans : Transition ) => {
484+ let promise , ids = trans [ cacheProp ] = trans [ cacheProp ] || { } ;
485+ if ( ! prevTruthyAnswer ( trans ) ) {
486+ promise = $q . when ( controllerInstance . uiCanExit ( trans ) ) ;
487+ promise . then ( val => ids [ id ] = ( val !== false ) ) ;
488+ }
489+ return promise ;
490+ } ;
491+
492+ let criteria = { exiting : viewState . name } ;
493+ $scope . $on ( '$destroy' , < any > $transitions . onBefore ( criteria , wrappedHook , hookOptions ) ) ;
464494 }
465495}
466496
0 commit comments