Skip to content

Commit 3641c2c

Browse files
refactor(Transition): Remove TransitionRunner class.
feat(Transition): Run onBefore hooks for *all* transitions created, even invalid ones.
1 parent 6e02905 commit 3641c2c

File tree

8 files changed

+72
-89
lines changed

8 files changed

+72
-89
lines changed

src/resolve/resolvable.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export default class Resolvable {
8989
}
9090

9191
toString() {
92-
return `Resolvable(name: ${this.name}, state: ${this.state.name}, requires: [${this.deps}])`;
92+
return `Resolvable(name: ${this.name}, requires: [${this.deps}])`;
9393
}
9494

9595
/**
@@ -100,6 +100,6 @@ export default class Resolvable {
100100
let invalid = filter(resolves, not(isFunction)), keys = Object.keys(invalid);
101101
if (keys.length)
102102
throw new Error(`Invalid resolve key/value: ${keys[0]}/${invalid[keys[0]]}`);
103-
return map(resolves, (fn, name) => new Resolvable(name, fn));
103+
return map(resolves, (fn, name: string) => new Resolvable(name, fn));
104104
}
105105
}

src/state/hooks/transitionManager.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,8 @@ export default class TransitionManager {
5151
this.resolveHooks = new ResolveHooks(transition);
5252

5353
this.treeChanges = transition.treeChanges();
54-
}
5554

56-
registerHooks() {
5755
this.registerUpdateGlobalState();
58-
5956
this.viewHooks.registerHooks();
6057
this.enterExitHooks.registerHooks();
6158
this.resolveHooks.registerHooks();
@@ -98,9 +95,7 @@ export default class TransitionManager {
9895

9996
if (error.type === RejectType.SUPERSEDED) {
10097
if (error.redirected && error.detail instanceof Transition) {
101-
let tMgr = this._redirectMgr(error.detail);
102-
tMgr.registerHooks();
103-
return tMgr.runTransition();
98+
return this._redirectMgr(error.detail).runTransition();
10499
}
105100
}
106101
}

src/state/state.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -725,13 +725,10 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactoryProvider) {
725725
return $q.reject(ref.error());
726726

727727
let transition = $transitions.create(currentPath, ref);
728-
if (!transition.valid())
729-
return $q.reject(transition.error());
730-
731728
let tMgr = new TransitionManager(transition, $transitions, $urlRouter, $view, $state, $stateParams, $q, transQueue, treeChangesQueue);
732-
tMgr.registerHooks();
729+
let transitionPromise = tMgr.runTransition();
733730
// Return a promise for the transition, which also has the transition object on it.
734-
return extend(tMgr.runTransition(), { transition });
731+
return extend(transitionPromise, { transition });
735732
};
736733

737734
/**

src/state/stateEvents.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {RejectType} from "../transition/rejectFactory";
1313

1414
stateChangeStartHandler.$inject = ['$transition$', '$stateEvents', '$rootScope', '$urlRouter'];
1515
function stateChangeStartHandler($transition$: Transition, $stateEvents, $rootScope, $urlRouter) {
16-
if (!$transition$.options().notify)
16+
if (!$transition$.options().notify || !$transition$.valid() || $transition$.ignored())
1717
return;
1818

1919
let enabledEvents = $stateEvents.provider.enabled();
@@ -46,15 +46,17 @@ function stateChangeStartHandler($transition$: Transition, $stateEvents, $rootSc
4646
let toParams = $transition$.params("to");
4747
let fromParams = $transition$.params("from");
4848

49-
if (enabledEvents.$stateChangeStart && $rootScope.$broadcast('$stateChangeStart', $transition$.to(), toParams, $transition$.from(), fromParams, $transition$).defaultPrevented) {
50-
if (enabledEvents.$stateChangeCancel) {
51-
$rootScope.$broadcast('$stateChangeCancel', $transition$.to(), toParams, $transition$.from(), fromParams, $transition$);
49+
if (enabledEvents.$stateChangeSuccess) {
50+
var startEvent = $rootScope.$broadcast('$stateChangeStart', $transition$.to(), toParams, $transition$.from(), fromParams, $transition$);
51+
52+
if (startEvent.defaultPrevented) {
53+
if (enabledEvents.$stateChangeCancel) {
54+
$rootScope.$broadcast('$stateChangeCancel', $transition$.to(), toParams, $transition$.from(), fromParams, $transition$);
55+
}
56+
$urlRouter.update();
57+
return false;
5258
}
53-
$urlRouter.update();
54-
return false;
55-
}
5659

57-
if (enabledEvents.$stateChangeSuccess) {
5860
$transition$.promise.then(function () {
5961
/**
6062
* @ngdoc event

src/transition/hookBuilder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export default class HookBuilder {
5555
// onBefore/onStart/onFinish/onSuccess/onError returns an array of hooks
5656
// onExit/onRetain/onEnter returns an array of arrays of hooks
5757

58-
getOnBeforeHooks = () => this._buildTransitionHooks("onBefore");
58+
getOnBeforeHooks = () => this._buildTransitionHooks("onBefore", {}, { async: false });
5959
getOnStartHooks = () => this._buildTransitionHooks("onStart");
6060
getOnExitHooks = () => this._buildNodeHooks("onExit", this.treeChanges.exiting.reverse(), (node) => this._toFrom({ from: node.state }));
6161
getOnRetainHooks = () => this._buildNodeHooks("onRetain", this.treeChanges.retained, (node) => this._toFrom());

src/transition/transition.ts

+24-8
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import trace from "../common/trace";
55

66
import {ITransitionOptions, ITransitionHookOptions, ITreeChanges, IHookRegistry, IHookRegistration, IHookGetter} from "./interface";
77
import $transitions from "./transitionService";
8+
import TransitionHook from "./transitionHook";
89
import {HookRegistry, matchState} from "./hookRegistry";
910
import HookBuilder from "./hookBuilder";
10-
import TransitionRunner from "./transitionRunner";
11-
import {RejectFactory} from "./rejectFactory";
11+
import {RejectFactory, TransitionRejection} from "./rejectFactory";
1212

1313
import Node from "../path/node";
1414
import PathFactory from "../path/pathFactory";
@@ -27,7 +27,8 @@ import {ViewConfig} from "../view/view";
2727

2828
import {
2929
map, find, extend, filter, mergeR, flatten, unnest, tail, forEach, identity,
30-
omit, isObject, not, prop, propEq, toJson, val, abstractKey, arrayTuples, allTrueR
30+
omit, isObject, isPromise, not, prop, propEq, toJson, val, abstractKey,
31+
arrayTuples, allTrueR, Predicate
3132
} from "../common/common";
3233

3334
let transitionCount = 0, REJECT = new RejectFactory();
@@ -164,7 +165,7 @@ export class Transition implements IHookRegistry {
164165
* @param state (optional) the state in the topath which should receive the new resolves (otherwise, the root state)
165166
*/
166167
addResolves(resolves: IResolveDeclarations, state: IStateOrName = "") {
167-
let stateName = state.name ? state.name : state;
168+
let stateName = <string> (<any> state).name ? (<any> state).name : state;
168169
let topath = this._treeChanges.to;
169170
let targetNode = find(topath, node => node.state.name === stateName);
170171
tail(topath).resolveContext.addResolvables(Resolvable.makeResolvables(resolves), targetNode.state);
@@ -309,14 +310,26 @@ export class Transition implements IHookRegistry {
309310
}
310311

311312
run () {
313+
let hookBuilder = this.hookBuilder();
314+
let runSynchronousHooks = TransitionHook.runSynchronousHooks;
315+
const runSuccessHooks = () => runSynchronousHooks(hookBuilder.getOnSuccessHooks(), {}, true);
316+
const runErrorHooks = ($error$) => runSynchronousHooks(hookBuilder.getOnErrorHooks(), { $error$ }, true);
317+
// Run the success/error hooks *after* the Transition promise is settled.
318+
this.promise.then(runSuccessHooks, runErrorHooks);
319+
320+
let promiseChain = runSynchronousHooks(hookBuilder.getOnBeforeHooks());
321+
322+
if (TransitionHook.isRejection(promiseChain)) {
323+
this._deferred.reject(promiseChain);
324+
return this.promise;
325+
}
326+
312327
if (!this.valid()) {
313328
let error = new Error(this.error());
314329
this._deferred.reject(error);
315-
throw error;
330+
return this.promise;
316331
}
317332

318-
trace.traceTransitionStart(this);
319-
320333
if (this.ignored()) {
321334
trace.traceTransitionIgnored(this);
322335
let ignored = REJECT.ignored();
@@ -336,7 +349,10 @@ export class Transition implements IHookRegistry {
336349
return runtime.$q.reject(error);
337350
};
338351

339-
new TransitionRunner(this, resolve, reject).run();
352+
trace.traceTransitionStart(this);
353+
354+
promiseChain = hookBuilder.asyncHooks().reduce((_chain, step) => _chain.then(step.invokeStep), promiseChain);
355+
promiseChain.then(resolve, reject);
340356

341357
return this.promise;
342358
}

src/transition/transitionHook.ts

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import {IPromise} from "angular";
12
import {IInjectable, defaults, extend, noop, filter, not, isFunction, isDefined, map, pattern, val,
2-
eq, is, isPromise, isObject, parse, fnToString, maxLength} from "../common/common";
3+
eq, is, isPromise, isObject, parse, fnToString, maxLength, Predicate} from "../common/common";
4+
import {runtime} from "../common/angular1";
35
import trace from "../common/trace";
4-
import {RejectFactory} from "./rejectFactory";
6+
import {RejectFactory, TransitionRejection} from "./rejectFactory";
57
import {Transition} from "./transition";
68
import {State} from "../state/state";
79
import {IResolveDeclarations} from "../state/interface";
@@ -78,4 +80,32 @@ export default class TransitionHook {
7880
name = fnToString(fn);
7981
return `${event} context: ${context}, ${maxLength(200, name)}`;
8082
}
83+
84+
85+
/**
86+
* Given an array of TransitionHooks, runs each one synchronously and sequentially.
87+
*
88+
* Returns a promise chain composed of any promises returned from each hook.invokeStep() call
89+
*/
90+
static runSynchronousHooks(hooks: TransitionHook[], locals = {}, swallowExceptions: boolean = false): IPromise<any> {
91+
let results = [];
92+
for (let i = 0; i < hooks.length; i++) {
93+
try {
94+
let hookResult = hooks[i].invokeStep(locals);
95+
let rejection = TransitionHook.isRejection(hookResult);
96+
if (rejection) return rejection;
97+
results.push(hookResult);
98+
} catch (exception) {
99+
if (!swallowExceptions) throw exception;
100+
console.log("Swallowed exception during synchronous hook handler: " + exception); // TODO: What to do here?
101+
}
102+
}
103+
104+
return results.filter(<Predicate<any>> isPromise).reduce((chain, promise) => chain.then(val(promise)), runtime.$q.when());
105+
}
106+
107+
108+
static isRejection(hookResult) {
109+
return hookResult && hookResult.reason instanceof TransitionRejection && hookResult;
110+
}
81111
}

src/transition/transitionRunner.ts

-57
This file was deleted.

0 commit comments

Comments
 (0)