Skip to content

Commit beedc82

Browse files
feat(Transition): Support treechange paths in API for Resolve+transition
- Allow resolve data to be retrieved for arbitrary tree change paths, including `to` and `from`, but default to the `to` path docs(Transition): add docs and mark some as @internalapi Closes angular-ui/ui-router#3081
1 parent 2c059c4 commit beedc82

File tree

2 files changed

+97
-16
lines changed

2 files changed

+97
-16
lines changed

src/transition/transition.ts

+78-16
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ import {Globals} from "../globals";
3232
import {UIInjector} from "../interface";
3333
import {RawParams} from "../params/interface";
3434

35-
35+
/** @hidden */
3636
let transitionCount = 0;
37+
/** @hidden */
3738
const stateSelf: (_state: State) => StateDeclaration = prop("self");
3839

3940
/**
@@ -45,8 +46,15 @@ const stateSelf: (_state: State) => StateDeclaration = prop("self");
4546
* It has information about all states being entered and exited as a result of the transition.
4647
*/
4748
export class Transition implements IHookRegistry {
49+
50+
/** @hidden */
4851
static diToken = Transition;
49-
52+
53+
/**
54+
* A unique identifier for the transition.
55+
*
56+
* This is an auto incrementing integer, starting from `0`.
57+
*/
5058
$id: number;
5159

5260
/**
@@ -75,8 +83,11 @@ export class Transition implements IHookRegistry {
7583
/** @hidden */
7684
private _error: any;
7785

86+
/** @hidden */
7887
private _options: TransitionOptions;
88+
/** @hidden */
7989
private _treeChanges: TreeChanges;
90+
/** @hidden */
8091
private _targetState: TargetState;
8192

8293
/** @inheritdoc */
@@ -103,6 +114,8 @@ export class Transition implements IHookRegistry {
103114
*
104115
* If the target state is not valid, an error is thrown.
105116
*
117+
* @internalapi
118+
*
106119
* @param fromPath The path of [[PathNode]]s from which the transition is leaving. The last node in the `fromPath`
107120
* encapsulates the "from state".
108121
* @param targetState The target state and parameters being transitioned to (also, the transition options)
@@ -139,18 +152,30 @@ export class Transition implements IHookRegistry {
139152
context.addResolvables(rootResolvables, rootNode.state);
140153
}
141154

155+
/**
156+
* @internalapi
157+
*
158+
* @returns the internal from [State] object
159+
*/
142160
$from() {
143161
return tail(this._treeChanges.from).state;
144162
}
145163

164+
/**
165+
* @internalapi
166+
*
167+
* @returns the internal to [State] object
168+
*/
146169
$to() {
147170
return tail(this._treeChanges.to).state;
148171
}
149172

150173
/**
151174
* Returns the "from state"
152175
*
153-
* @returns The state object for the Transition's "from state".
176+
* Returns the state that the transition is coming *from*.
177+
*
178+
* @returns The state declaration object for the Transition's ("from state").
154179
*/
155180
from(): StateDeclaration {
156181
return this.$from().self;
@@ -159,7 +184,9 @@ export class Transition implements IHookRegistry {
159184
/**
160185
* Returns the "to state"
161186
*
162-
* @returns The state object for the Transition's target state ("to state").
187+
* Returns the state that the transition is going *from*.
188+
*
189+
* @returns The state declaration object for the Transition's target state ("to state").
163190
*/
164191
to() {
165192
return this.$to().self;
@@ -168,7 +195,7 @@ export class Transition implements IHookRegistry {
168195
/**
169196
* Gets the Target State
170197
*
171-
* A transition's [[TargetState]] encapsulates the [[to]] state, the [[params]], and the [[options]].
198+
* A transition's [[TargetState]] encapsulates the [[to]] state, the [[params]], and the [[options]] as a single object.
172199
*
173200
* @returns the [[TargetState]] of this Transition
174201
*/
@@ -193,8 +220,14 @@ export class Transition implements IHookRegistry {
193220
/**
194221
* Gets transition parameter values
195222
*
196-
* @param pathname Pick which treeChanges path to get parameters for:
223+
* Returns the parameter values for a transition as key/value pairs.
224+
*
225+
* By default, returns the new parameter values (for the "to state").
226+
* To return the previous parameter values, supply `'from'` as the `pathname` argument.
227+
*
228+
* @param pathname the name of the treeChanges path to get parameter values for:
197229
* (`'to'`, `'from'`, `'entering'`, `'exiting'`, `'retained'`)
230+
*
198231
* @returns transition parameter values for the desired path.
199232
*/
200233
params(pathname: string = "to"): { [key: string]: any } {
@@ -216,7 +249,7 @@ export class Transition implements IHookRegistry {
216249
* @returns a [[UIInjector]]
217250
*/
218251
injector(state?: StateOrName): UIInjector {
219-
let path: PathNode[] = this.treeChanges().to;
252+
let path: PathNode[] = this._treeChanges.to;
220253
if (state) path = PathFactory.subPath(path, node => node.state === state || node.state.name === state);
221254
return new ResolveContext(path).injector();
222255
}
@@ -230,10 +263,12 @@ export class Transition implements IHookRegistry {
230263
* The returned tokens include those defined on [[StateDeclaration.resolve]] blocks, for the states
231264
* in the Transition's [[TreeChanges.to]] path.
232265
*
266+
* @param pathname resolve context's path name (e.g., `to` or `from`)
267+
*
233268
* @returns an array of resolve tokens (keys)
234269
*/
235-
getResolveTokens(): any[] {
236-
return new ResolveContext(this._treeChanges.to).getTokens();
270+
getResolveTokens(pathname: string = "to"): any[] {
271+
return new ResolveContext(this._treeChanges[pathname]).getTokens();
237272
}
238273

239274

@@ -250,11 +285,14 @@ export class Transition implements IHookRegistry {
250285
* If a resolvable doesn't exist for the token, throws an error.
251286
*
252287
* @param token the token (or array of tokens)
288+
* @param pathname resolve context's path name (e.g., `to` or `from`)
253289
*
254290
* @returns an array of resolve tokens (keys)
255291
*/
256-
getResolveValue(token: (any|any[])): (any|any[]) {
257-
let resolveContext = new ResolveContext(this._treeChanges.to);
292+
getResolveValue(token: any[], pathname?: string): any[];
293+
getResolveValue(token: any, pathname?: string): any;
294+
getResolveValue(token: (any|any[]), pathname: string = "to"): (any|any[]) {
295+
let resolveContext = new ResolveContext(this._treeChanges[pathname]);
258296
const getData = (token: any) => {
259297
var resolvable = resolveContext.getResolvable(token);
260298
if (resolvable === undefined) {
@@ -276,11 +314,12 @@ export class Transition implements IHookRegistry {
276314
* This is a lower level API that returns a [[Resolvable]] from the Transition for a given token.
277315
*
278316
* @param token the DI token
317+
* @param pathname resolve context's path name (e.g., `to` or `from`)
279318
*
280319
* @returns the [[Resolvable]] in the transition's to path, or undefined
281320
*/
282-
getResolvable(token: any): Resolvable {
283-
return new ResolveContext(this._treeChanges.to).getResolvable(token);
321+
getResolvable(token: any, pathname = "to"): Resolvable {
322+
return new ResolveContext(this._treeChanges[pathname]).getResolvable(token);
284323
}
285324

286325
/**
@@ -374,23 +413,41 @@ export class Transition implements IHookRegistry {
374413
return path.map(prop("views")).filter(identity).reduce(unnestR, []);
375414
}
376415

377-
treeChanges = () => this._treeChanges;
416+
/**
417+
* Return the transition's tree changes
418+
*
419+
* A transition goes from one state/parameters to another state/parameters.
420+
* During a transition, states are entered and/or exited.
421+
*
422+
* This function returns various branches (paths) which represent the changes to the
423+
* active state tree that are caused by the transition.
424+
*
425+
* @param pathname The name of the tree changes path to get:
426+
* (`'to'`, `'from'`, `'entering'`, `'exiting'`, `'retained'`)
427+
*/
428+
treeChanges(pathname: string): PathNode[];
429+
treeChanges(): TreeChanges;
430+
treeChanges(pathname?: string) {
431+
return pathname ? this._treeChanges[pathname] : this._treeChanges;
432+
}
378433

379434
/**
380435
* Creates a new transition that is a redirection of the current one.
381436
*
382437
* This transition can be returned from a [[TransitionService]] hook to
383438
* redirect a transition to a new state and/or set of parameters.
384439
*
440+
* @internalapi
441+
*
385442
* @returns Returns a new [[Transition]] instance.
386443
*/
387444
redirect(targetState: TargetState): Transition {
388445
let newOptions = extend({}, this.options(), targetState.options(), { redirectedFrom: this, source: "redirect" });
389446
targetState = new TargetState(targetState.identifier(), targetState.$state(), targetState.params(), newOptions);
390447

391448
let newTransition = this.router.transitionService.create(this._treeChanges.from, targetState);
392-
let originalEnteringNodes = this.treeChanges().entering;
393-
let redirectEnteringNodes = newTransition.treeChanges().entering;
449+
let originalEnteringNodes = this._treeChanges.entering;
450+
let redirectEnteringNodes = newTransition._treeChanges.entering;
394451

395452
// --- Re-use resolve data from original transition ---
396453
// When redirecting from a parent state to a child state where the parent parameter values haven't changed
@@ -469,6 +526,8 @@ export class Transition implements IHookRegistry {
469526
*
470527
* This method is generally called from the [[StateService.transitionTo]]
471528
*
529+
* @internalapi
530+
*
472531
* @returns a promise for a successful transition.
473532
*/
474533
run(): Promise<any> {
@@ -529,6 +588,9 @@ export class Transition implements IHookRegistry {
529588
return this.promise;
530589
}
531590

591+
/**
592+
* Checks if this transition is currently active/running.
593+
*/
532594
isActive = () => this === this._options.current();
533595

534596
/**

test/transitionSpec.ts

+19
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,25 @@ describe('transition', function () {
345345
})
346346
.then(done);
347347
}));
348+
349+
// test for #3081
350+
it('should inject resolve values from the exited state', ((done) => {
351+
router.stateRegistry.register({
352+
name: 'design',
353+
url: '/design',
354+
resolve: { cc: () => 'cc resolve' },
355+
onExit: (trans, state) => {
356+
expect(state.self).toBe(router.stateRegistry.get('design'));
357+
expect(trans.getResolveValue('cc', 'from')).toBe('cc resolve');
358+
done();
359+
}
360+
});
361+
const $state = router.stateService;
362+
363+
Promise.resolve()
364+
.then(() => $state.go("design"))
365+
.then(() => $state.go("A"));
366+
}));
348367
});
349368

350369
describe('.onSuccess()', function() {

0 commit comments

Comments
 (0)