@@ -335,9 +335,14 @@ function $ViewDirective($view: ViewService, $animate: any, $uiViewScroll: any, $
335
335
return directive ;
336
336
} ] ;
337
337
338
- $ViewDirectiveFill . $inject = [ '$compile' , '$controller' , '$transitions' , '$view' , '$timeout' ] ;
338
+ $ViewDirectiveFill . $inject = [ '$compile' , '$controller' , '$transitions' , '$view' , '$q' , '$ timeout'] ;
339
339
/** @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 ) {
341
346
const getControllerAs = parse ( 'viewDecl.controllerAs' ) ;
342
347
const getResolveAs = parse ( 'viewDecl.resolveAs' ) ;
343
348
@@ -384,7 +389,7 @@ function $ViewDirectiveFill ($compile: ICompileService, $controller: IController
384
389
$element . data ( '$ngControllerController' , controllerInstance ) ;
385
390
$element . children ( ) . data ( '$ngControllerController' , controllerInstance ) ;
386
391
387
- registerControllerCallbacks ( $transitions , controllerInstance , scope , cfg ) ;
392
+ registerControllerCallbacks ( $q , $ transitions, controllerInstance , scope , cfg ) ;
388
393
}
389
394
390
395
// Wait for the component to appear in the DOM
@@ -402,7 +407,7 @@ function $ViewDirectiveFill ($compile: ICompileService, $controller: IController
402
407
403
408
let deregisterWatch = scope . $watch ( getComponentController , function ( ctrlInstance ) {
404
409
if ( ! ctrlInstance ) return ;
405
- registerControllerCallbacks ( $transitions , ctrlInstance , scope , cfg ) ;
410
+ registerControllerCallbacks ( $q , $ transitions, ctrlInstance , scope , cfg ) ;
406
411
deregisterWatch ( ) ;
407
412
} ) ;
408
413
}
@@ -415,15 +420,23 @@ function $ViewDirectiveFill ($compile: ICompileService, $controller: IController
415
420
416
421
/** @hidden */
417
422
let hasComponentImpl = typeof ( angular as any ) . module ( 'ui.router' ) [ 'component' ] === 'function' ;
423
+ /** @hidden incrementing id */
424
+ let _uiCanExitId = 0 ;
418
425
419
426
/** @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 ) {
421
432
// 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
+ }
423
436
424
437
let viewState : Ng1StateDeclaration = tail ( cfg . path ) . state . self ;
425
438
426
- var hookOptions : HookRegOptions = { bind : controllerInstance } ;
439
+ let hookOptions : HookRegOptions = { bind : controllerInstance } ;
427
440
// Add component-level hook for onParamsChange
428
441
if ( isFunction ( controllerInstance . uiOnParamsChanged ) ) {
429
442
let resolveContext : ResolveContext = new ResolveContext ( cfg . path ) ;
@@ -450,7 +463,7 @@ function registerControllerCallbacks($transitions: TransitionService, controller
450
463
if ( changedToParams . length ) {
451
464
let changedKeys : string [ ] = changedToParams . map ( x => x . id ) ;
452
465
// 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 ) ;
454
467
controllerInstance . uiOnParamsChanged ( newValues , $transition$ ) ;
455
468
}
456
469
} ;
@@ -459,8 +472,25 @@ function registerControllerCallbacks($transitions: TransitionService, controller
459
472
460
473
// Add component-level hook for uiCanExit
461
474
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 ) ) ;
464
494
}
465
495
}
466
496
0 commit comments