11/** @module state */ /** */
2- import { extend , defaults , silentRejection , silenceUncaughtInPromise } from "../common/common" ;
2+ import { extend , defaults , silentRejection , silenceUncaughtInPromise , removeFrom } from "../common/common" ;
33import { isDefined , isObject , isString } from "../common/predicates" ;
44import { Queue } from "../common/queue" ;
55import { services } from "../common/coreservices" ;
@@ -25,9 +25,16 @@ import {HrefOptions} from "./interface";
2525import { bindFunctions } from "../common/common" ;
2626import { Globals } from "../globals" ;
2727import { UIRouter } from "../router" ;
28- import { StateParams } from "../params/stateParams" ; // for params() return type
28+ import { UIInjector } from "../common/interface" ;
29+ import { ResolveContext } from "../resolve/resolveContext" ;
30+ import { StateParams } from "../params/stateParams" ; // has or is using
31+
32+ export type OnInvalidCallback =
33+ ( toState ?: TargetState , fromState ?: TargetState , injector ?: UIInjector ) => HookResult ;
2934
3035export class StateService {
36+ invalidCallbacks : OnInvalidCallback [ ] = [ ] ;
37+
3138 get transition ( ) { return this . router . globals . transition ; }
3239 get params ( ) { return this . router . globals . params ; }
3340 get current ( ) { return this . router . globals . current ; }
@@ -49,16 +56,13 @@ export class StateService {
4956 *
5057 * If a callback returns an TargetState, then it is used as arguments to $state.transitionTo() and the result returned.
5158 */
52- private _handleInvalidTargetState ( fromPath : PathNode [ ] , $to$ : TargetState ) {
59+ private _handleInvalidTargetState ( fromPath : PathNode [ ] , toState : TargetState ) {
60+ let fromState = PathFactory . makeTargetState ( fromPath ) ;
5361 let globals = < Globals > this . router . globals ;
5462 const latestThing = ( ) => globals . transitionHistory . peekTail ( ) ;
5563 let latest = latestThing ( ) ;
56- let $from$ = PathFactory . makeTargetState ( fromPath ) ;
57- let callbackQueue = new Queue < Function > ( this . router . stateProvider . invalidCallbacks . slice ( ) ) ;
58- let { $q, $injector} = services ;
59-
60- const invokeCallback = ( callback : Function ) =>
61- $q . when ( $injector . invoke ( callback , null , { $to$, $from$ } ) ) ;
64+ let callbackQueue = new Queue < OnInvalidCallback > ( this . invalidCallbacks . slice ( ) ) ;
65+ let injector = new ResolveContext ( fromPath ) . injector ( ) ;
6266
6367 const checkForRedirect = ( result : HookResult ) => {
6468 if ( ! ( result instanceof TargetState ) ) {
@@ -76,13 +80,47 @@ export class StateService {
7680
7781 function invokeNextCallback ( ) {
7882 let nextCallback = callbackQueue . dequeue ( ) ;
79- if ( nextCallback === undefined ) return Rejection . invalid ( $to$ . error ( ) ) . toPromise ( ) ;
80- return invokeCallback ( nextCallback ) . then ( checkForRedirect ) . then ( result => result || invokeNextCallback ( ) ) ;
83+ if ( nextCallback === undefined ) return Rejection . invalid ( toState . error ( ) ) . toPromise ( ) ;
84+
85+ let callbackResult = services . $q . when ( nextCallback ( toState , fromState , injector ) ) ;
86+ return callbackResult . then ( checkForRedirect ) . then ( result => result || invokeNextCallback ( ) ) ;
8187 }
8288
8389 return invokeNextCallback ( ) ;
8490 }
8591
92+ /**
93+ * Registers an Invalid State handler
94+ *
95+ * Registers a [[OnInvalidCallback]] function to be invoked when [[StateService.transitionTo]]
96+ * has been called with an invalid state reference parameter
97+ *
98+ * Example:
99+ * ```js
100+ * stateService.onInvalid(function(to, from, injector) {
101+ * if (to.name() === 'foo') {
102+ * let lazyLoader = injector.get('LazyLoadService');
103+ * return lazyLoader.load('foo')
104+ * .then(() => stateService.target('foo'));
105+ * }
106+ * });
107+ * ```
108+ *
109+ * @param {function } callback invoked when the toState is invalid
110+ * This function receives the (invalid) toState, the fromState, and an injector.
111+ * The function may optionally return a [[TargetState]] or a Promise for a TargetState.
112+ * If one is returned, it is treated as a redirect.
113+ *
114+ * @returns a function which deregisters the callback
115+ */
116+ onInvalid ( callback : OnInvalidCallback ) : Function {
117+ this . invalidCallbacks . push ( callback ) ;
118+ return function deregisterListener ( ) {
119+ removeFrom ( this . invalidCallbacks ) ( callback ) ;
120+ } . bind ( this ) ;
121+ }
122+
123+
86124 /**
87125 * @ngdoc function
88126 * @name ui.router.state.$state#reload
0 commit comments