2
2
import { trace } from "../common/trace" ;
3
3
import { services } from "../common/coreservices" ;
4
4
import {
5
- map , find , extend , filter , mergeR , tail ,
5
+ map , find , extend , mergeR , tail ,
6
6
omit , toJson , abstractKey , arrayTuples , unnestR , identity , anyTrueR
7
7
} from "../common/common" ;
8
8
import { isObject } from "../common/predicates" ;
9
- import { not , prop , propEq , val } from "../common/hof" ;
9
+ import { prop , propEq , val , not } from "../common/hof" ;
10
10
11
11
import { StateDeclaration , StateOrName } from "../state/interface" ;
12
12
import { TransitionOptions , TransitionHookOptions , TreeChanges , IHookRegistry , IHookRegistration , IHookGetter } from "./interface" ;
@@ -20,7 +20,6 @@ import {Resolvable} from "../resolve/module";
20
20
import { TransitionService } from "./transitionService" ;
21
21
import { ViewConfig } from "../view/interface" ;
22
22
import { Rejection } from "./rejectFactory" ;
23
- import { Resolvables } from "../resolve/interface" ;
24
23
25
24
26
25
let transitionCount = 0 ;
@@ -143,10 +142,10 @@ export class Transition implements IHookRegistry {
143
142
144
143
PathFactory . bindResolveContexts ( this . _treeChanges . to ) ;
145
144
146
- let rootResolvables : Resolvables = {
147
- "$transition$" : new Resolvable ( '$transition$' , ( ) => this , this ) ,
148
- "$stateParams" : new Resolvable ( '$stateParams' , ( ) => this . params ( ) , this . params ( ) )
149
- } ;
145
+ let rootResolvables : Resolvable [ ] = [
146
+ new Resolvable ( '$transition$' , ( ) => this , this ) ,
147
+ new Resolvable ( '$stateParams' , ( ) => this . params ( ) , this . params ( ) )
148
+ ] ;
150
149
let rootNode : Node = this . _treeChanges . to [ 0 ] ;
151
150
rootNode . resolveContext . addResolvables ( rootResolvables , rootNode . state )
152
151
}
@@ -292,34 +291,45 @@ export class Transition implements IHookRegistry {
292
291
treeChanges = ( ) => this . _treeChanges ;
293
292
294
293
/**
295
- * @ngdoc function
296
- * @name ui.router.state.type:Transition#redirect
297
- * @methodOf ui.router.state.type:Transition
294
+ * Creates a new transition that is a redirection of the current one.
298
295
*
299
- * @description
300
- * Creates a new transition that is a redirection of the current one. This transition can
301
- * be returned from a `$transitionsProvider` hook, `$state` event, or other method, to
296
+ * This transition can be returned from a [[TransitionService]] hook to
302
297
* redirect a transition to a new state and/or set of parameters.
303
298
*
304
- * @returns { Transition } Returns a new ` Transition` instance.
299
+ * @returns Returns a new [[ Transition]] instance.
305
300
*/
306
301
redirect ( targetState : TargetState ) : Transition {
307
302
let newOptions = extend ( { } , this . options ( ) , targetState . options ( ) , { previous : this } ) ;
308
303
targetState = new TargetState ( targetState . identifier ( ) , targetState . $state ( ) , targetState . params ( ) , newOptions ) ;
309
304
310
- let redirectTo = new Transition ( this . _treeChanges . from , targetState , this . _transitionService ) ;
311
- let reloadState = targetState . options ( ) . reloadState ;
305
+ let newTransition = new Transition ( this . _treeChanges . from , targetState , this . _transitionService ) ;
306
+ let originalEnteringNodes = this . treeChanges ( ) . entering ;
307
+ let redirectEnteringNodes = newTransition . treeChanges ( ) . entering ;
308
+
309
+ // --- Re-use resolve data from original transition ---
310
+ // When redirecting from a parent state to a child state where the parent parameter values haven't changed
311
+ // (because of the redirect), the resolves fetched by the original transition are still valid in the
312
+ // redirected transition.
313
+ //
314
+ // This allows you to define a redirect on a parent state which depends on an async resolve value.
315
+ // You can wait for the resolve, then redirect to a child state based on the result.
316
+ // The redirected transition does not have to re-fetch the resolve.
317
+ // ---------------------------------------------------------
318
+
319
+ const nodeIsReloading = ( reloadState : State ) => ( node : Node ) => {
320
+ return reloadState && reloadState . includes [ node . state . name ] ;
321
+ } ;
322
+
323
+ // Find any "entering" nodes in the redirect path that match the original path and aren't being reloaded
324
+ let matchingEnteringNodes : Node [ ] = Node . matching ( redirectEnteringNodes , originalEnteringNodes )
325
+ . filter ( not ( nodeIsReloading ( targetState . options ( ) . reloadState ) ) ) ;
312
326
313
- // If the current transition has already resolved any resolvables which are also in the redirected "to path", then
314
- // add those resolvables to the redirected transition. Allows you to define a resolve at a parent level, wait for
315
- // the resolve, then redirect to a child state based on the result, and not have to re-fetch the resolve.
316
- let redirectedPath = this . treeChanges ( ) . to ;
317
- let copyResolvesFor : Node [ ] = Node . matching ( redirectTo . treeChanges ( ) . to , redirectedPath )
318
- . filter ( node => ! reloadState || ! reloadState . includes [ node . state . name ] ) ;
319
- const includeResolve = ( resolve , key ) => [ '$stateParams' , '$transition$' ] . indexOf ( key ) === - 1 ;
320
- copyResolvesFor . forEach ( ( node , idx ) => extend ( node . resolves , filter ( redirectedPath [ idx ] . resolves , includeResolve ) ) ) ;
327
+ // Use the existing (possibly pre-resolved) resolvables for the matching entering nodes.
328
+ matchingEnteringNodes . forEach ( ( node , idx ) => {
329
+ node . resolvables = originalEnteringNodes [ idx ] . resolvables ;
330
+ } ) ;
321
331
322
- return redirectTo ;
332
+ return newTransition ;
323
333
}
324
334
325
335
/** @hidden If a transition doesn't exit/enter any states, returns any [[Param]] whose value changed */
0 commit comments