Skip to content

Commit ca45957

Browse files
BC-BREAK: Transition Hooks are no longer injected (onBefore/onStart/onExit/onRetain/onEnter/onFinish/onSuccess/onError)
Previously, a hook like `['$state', ($state) => $state.target('foo')]` would get `$state` injected. Now, all hooks receive three parameters: - transition: the current Transition - injector: the $injector (ng1) or Injector (ng2) or fake injector (non-angular) - state: for onEnter/onRetain/onExit hooks only, the State which the hook is being run for. This value will be null for onBefore/onStart/onFinish/onSuccess/onError hooks. Refactor your hooks from: `['$state', ($state) => $state.target('foo')]` to: `(trans, injector) => injector.get('$state').target('foo')` feat(onEnter): Hooks receive 3 args: (Transition, Injector, State) and are not injectable feat(onEnter): state declaration onEnter hooks are still injected
1 parent 3f78850 commit ca45957

28 files changed

+588
-458
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
"test:ng1": "karma start config/karma.ng15.js",
1313
"test:ng1:debug": "karma start config/karma.ng15.js --singleRun=false --browsers=Chrome --autoWatch=true --autoWatchInterval=1",
14+
"test:ng1:watch": "node_modules/watch/cli.js 'npm run test:ng1' src test",
1415

1516
"test:ng12": "karma start config/karma.ng12.js",
1617
"test:ng13": "karma start config/karma.ng13.js",

src/common/strings.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export function fnToString(fn: IInjectable) {
6868
const isTransitionRejectionPromise = Rejection.isTransitionRejectionPromise;
6969

7070
let stringifyPatternFn = null;
71-
let stringifyPattern = function(val) {
71+
let stringifyPattern = function(value) {
7272
stringifyPatternFn = stringifyPatternFn || pattern([
7373
[not(isDefined), val("undefined")],
7474
[isNull, val("null")],
@@ -81,7 +81,7 @@ let stringifyPattern = function(val) {
8181
[val(true), identity]
8282
]);
8383

84-
return stringifyPatternFn(val);
84+
return stringifyPatternFn(value);
8585
};
8686

8787
export function stringify(o: Object) {

src/globals.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export class UIRouterGlobals {
5050
successfulTransitions = new Queue<Transition>([], 1);
5151

5252
constructor(transitionService: TransitionService) {
53-
const beforeNewTransition = ($transition$) => {
53+
const beforeNewTransition = ($transition$: Transition) => {
5454

5555
this.transition = $transition$;
5656
this.transitionHistory.enqueue($transition$);
@@ -70,6 +70,6 @@ export class UIRouterGlobals {
7070

7171
};
7272

73-
transitionService.onBefore({}, ['$transition$', beforeNewTransition]);
73+
transitionService.onBefore({}, beforeNewTransition);
7474
}
7575
}

src/ng1.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
export * from "./core";
88

99
export * from "./ng1/services";
10-
export * from "./ng1/viewsBuilder";
11-
export * from "./ng1/resolvesBuilder";
10+
export * from "./ng1/statebuilders/views";
11+
export * from "./ng1/statebuilders/resolve";
1212

1313
import "./ng1/stateDirectives";
1414
import "./ng1/stateFilters";

src/ng1/services.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ import {Resolvable} from "../resolve/resolvable";
2020
import {ResolveContext} from "../resolve/resolveContext";
2121
import {State} from "../state/stateObject";
2222
import {trace} from "../common/trace";
23-
import {ng1ViewsBuilder, ng1ViewConfigFactory, Ng1ViewConfig} from "./viewsBuilder";
23+
import {ng1ViewsBuilder, ng1ViewConfigFactory, Ng1ViewConfig} from "./statebuilders/views";
2424
import {TemplateFactory} from "./templateFactory";
25-
import {ng1ResolveBuilder} from "./resolvesBuilder";
25+
import {ng1ResolveBuilder} from "./statebuilders/resolve";
2626
import {StateParams} from "../params/stateParams";
2727
import {TransitionService} from "../transition/transitionService";
2828
import {StateService} from "../state/stateService";
2929
import {StateProvider} from "../state/state";
3030
import {UrlRouterProvider, UrlRouter} from "../url/urlRouter";
3131
import {UrlMatcherFactory} from "../url/urlMatcherFactory";
32+
import {Transition} from "../transition/transition";
33+
import {getStateHookBuilder} from "./statebuilders/onEnterExitRetain";
3234

3335
/** @hidden */
3436
let app = angular.module("ui.router.angular1", []);
@@ -166,9 +168,12 @@ function ng1UIRouter($locationProvider) {
166168
// Create a new instance of the Router when the ng1UIRouterProvider is initialized
167169
router = new UIRouter();
168170

169-
// Apply ng1 `views` builder to the StateBuilder
171+
// Apply ng1 specific StateBuilder code for `views`, `resolve`, and `onExit/Retain/Enter` properties
170172
router.stateRegistry.decorator("views", ng1ViewsBuilder);
171173
router.stateRegistry.decorator("resolve", ng1ResolveBuilder);
174+
router.stateRegistry.decorator("onExit", getStateHookBuilder("onExit"));
175+
router.stateRegistry.decorator("onRetain", getStateHookBuilder("onRetain"));
176+
router.stateRegistry.decorator("onEnter", getStateHookBuilder("onEnter"));
172177

173178
router.viewService.viewConfigFactory('ng1', ng1ViewConfigFactory);
174179

@@ -279,8 +284,8 @@ angular.module('ui.router.state').factory('$stateParams', ['ng1UIRouter', (ng1UI
279284

280285
// $transitions service and $transitionsProvider
281286
function getTransitionsProvider() {
282-
loadAllControllerLocals.$inject = ['$transition$'];
283-
function loadAllControllerLocals($transition$) {
287+
// TODO: check if we can remove loadAllControllerLocals. Shouldn't be necessary without JIT resolve policy
288+
function loadAllControllerLocals($transition$: Transition) {
284289
const loadLocals = (vc: Ng1ViewConfig) => {
285290
let node = (<Node> find($transition$.treeChanges().to, propEq('state', vc.viewDecl.$context)));
286291
// Temporary fix; This whole callback should be nuked when fixing #2662
@@ -297,7 +302,7 @@ function getTransitionsProvider() {
297302
.then(() => vc.locals = map(resolvables, res => res.data));
298303
};
299304

300-
let loadAllLocals = $transition$.views("entering").filter(vc => !!vc.controller).map(loadLocals);
305+
let loadAllLocals = $transition$.views("entering").filter(vc => !!(<Ng1ViewConfig>vc).controller).map(loadLocals);
301306
return services.$q.all(loadAllLocals).then(noop);
302307
}
303308
router.transitionService.onFinish({}, loadAllControllerLocals);

src/ng1/stateDirectives.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,7 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate, $transitio
334334
};
335335

336336
$scope.$on('$stateChangeSuccess', update);
337-
let updateAfterTransition = ['$transition$', function($transition$) { $transition$.promise.then(update); }];
338-
let deregisterFn = $transitions.onStart({}, updateAfterTransition);
339-
$scope.$on('$destroy', deregisterFn);
337+
$scope.$on('$destroy', $transitions.onStart({}, (trans) => trans.promise.then(update) && null));
340338

341339
function addState(stateName, stateParams, activeClass) {
342340
var state = $state.get(stateName, stateContext($element));

src/ng1/stateEvents.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,15 @@ var $stateNotFound;
153153
return memo;
154154
}
155155

156-
stateChangeStartHandler.$inject = ['$transition$', '$stateEvents', '$rootScope', '$state', '$urlRouter'];
157-
function stateChangeStartHandler($transition$: Transition, $stateEvents, $rootScope, $state, $urlRouter) {
156+
function stateChangeStartHandler($transition$: Transition, $injector) {
158157
if (!$transition$.options().notify || !$transition$.valid() || $transition$.ignored())
159158
return;
160159

160+
let $stateEvents = $injector.get('$stateEvents');
161+
let $rootScope = $injector.get('$rootScope');
162+
let $state = $injector.get('$state');
163+
let $urlRouter = $injector.get('$urlRouter');
164+
161165
let enabledEvents = $stateEvents.provider.enabled();
162166

163167

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/** @module ng1 */ /** */
2+
import {State} from "../../state/stateObject";
3+
import {TransitionStateHookFn, HookResult} from "../../transition/interface";
4+
import {Transition} from "../../transition/transition";
5+
import IInjectorService = angular.auto.IInjectorService;
6+
import {services} from "../../common/coreservices";
7+
8+
/**
9+
* This is a [[StateBuilder.builder]] function for angular1 `onEnter`, `onExit`,
10+
* `onRetain` callback hooks on a [[Ng1StateDeclaration]].
11+
*
12+
* When the [[StateBuilder]] builds a [[State]] object from a raw [[StateDeclaration]], this builder
13+
* ensures that those hooks are injectable for angular-ui-router (ng1).
14+
*/
15+
export const getStateHookBuilder = (hookName) =>
16+
function stateHookBuilder(state: State, parentFn): TransitionStateHookFn {
17+
let hook = state[hookName];
18+
if (!hook) return;
19+
20+
return function decoratedNg1Hook(trans: Transition, inj: IInjectorService): HookResult {
21+
services.$injector.invoke(hook, this, trans.resolves());
22+
}
23+
};

src/ng1/resolvesBuilder.ts renamed to src/ng1/statebuilders/resolve.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** @module ng1 */ /** */
2-
import {State} from "../state/stateObject";
3-
import {forEach} from "../common/common";
4-
import {isString} from "../common/predicates";
2+
import {State} from "../../state/stateObject";
3+
import {forEach} from "../../common/common";
4+
import {isString} from "../../common/predicates";
55

66
/**
77
* This is a [[StateBuilder.builder]] function for angular1 `resolve:` block on a [[Ng1StateDeclaration]].

src/ng1/viewsBuilder.ts renamed to src/ng1/statebuilders/views.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
/** @module ng1 */ /** */
2-
import {State} from "../state/stateObject";
3-
import {pick, forEach, anyTrueR, unnestR} from "../common/common";
4-
import {kebobString} from "../common/strings";
5-
import {ViewConfig} from "../view/interface";
6-
import {Ng1ViewDeclaration} from "./interface";
7-
import {ViewService} from "../view/view";
8-
import {isInjectable, isDefined, isString, isObject} from "../common/predicates";
9-
import {services} from "../common/coreservices";
10-
import {trace} from "../common/trace";
11-
import {Node} from "../path/node";
12-
import {TemplateFactory} from "./templateFactory";
13-
import {ResolveContext} from "../resolve/resolveContext";
2+
import {State} from "../../state/stateObject";
3+
import {pick, forEach, anyTrueR, unnestR} from "../../common/common";
4+
import {kebobString} from "../../common/strings";
5+
import {ViewConfig} from "../../view/interface";
6+
import {Ng1ViewDeclaration} from "../interface";
7+
import {ViewService} from "../../view/view";
8+
import {isInjectable, isDefined, isString, isObject} from "../../common/predicates";
9+
import {services} from "../../common/coreservices";
10+
import {trace} from "../../common/trace";
11+
import {Node} from "../../path/node";
12+
import {TemplateFactory} from "../templateFactory";
13+
import {ResolveContext} from "../../resolve/resolveContext";
1414

1515
export const ng1ViewConfigFactory = (node, view) => new Ng1ViewConfig(node, view);
1616

src/ng1/viewDirective.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import {extend, map, unnestR, filter} from "../common/common";
44
import {isDefined, isFunction, isString} from "../common/predicates";
55
import {trace} from "../common/trace";
66
import {ActiveUIView} from "../view/interface";
7-
import {Ng1ViewConfig} from "./viewsBuilder";
8-
import {RejectType} from "../transition/rejectFactory";
7+
import {Ng1ViewConfig} from "./statebuilders/views";
98
import {TransitionService} from "../transition/transitionService";
109
import {parse} from "../common/hof";
1110
import {ResolveContext} from "../resolve/resolveContext";
@@ -418,13 +417,7 @@ function registerControllerCallbacks($transitions: TransitionService, controller
418417
controllerInstance.uiOnParamsChanged(filter(toParams, (val, key) => changedKeys.indexOf(key) !== -1), $transition$);
419418
}
420419
};
421-
$scope.$on('$destroy', $transitions.onSuccess({}, ['$transition$', paramsUpdated]), hookOptions);
422-
423-
// Fire callback on any IGNORED transition
424-
let onDynamic = ($error$, $transition$) => {
425-
if ($error$.type === RejectType.IGNORED) paramsUpdated($transition$);
426-
};
427-
$scope.$on('$destroy', $transitions.onError({}, ['$error$', '$transition$', onDynamic]), hookOptions);
420+
$scope.$on('$destroy', $transitions.onSuccess({}, paramsUpdated, hookOptions));
428421
}
429422

430423
// Add component-level hook for uiCanExit

src/ng2/uiRouterConfig.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ import {UIRouter} from "../router";
4747
*
4848
* // hooks.ts
4949
* export function registerAuthHook(transitionService: TransitionService) {
50-
* let requireAuthentication = ($state, AuthService) {
51-
* if (!AuthService.isAuthenticated()) {
52-
* return $state.target('login');
50+
* let requireAuthentication = (transition: Transition, injector: Injector) {
51+
* if (!Injector.get(AuthService).isAuthenticated()) {
52+
* return Injector.get(StateService).target('login');
5353
* }
5454
* }
5555
* transitionService.onBefore({ to: (state) => state.requiresAuth }, requireAuthentication);

src/resolve/resolveContext.ts

+13
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,19 @@ export class ResolveContext {
173173
let resolvedLocals = map(resolvables, prop("data"));
174174
return services.$injector.invoke(<Function> fn, options.bind || null, extend({}, locals, resolvedLocals));
175175
}
176+
177+
injector(): { get(any): any } {
178+
179+
let get = (token: any) => {
180+
var resolvables = this.getResolvables();
181+
if (resolvables.hasOwnProperty(token)) {
182+
return resolvables[token].data;
183+
}
184+
return services.$injector.get(token);
185+
};
186+
187+
return {get};
188+
}
176189
}
177190

178191
/**

src/state/hooks/enterExitHooks.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,20 @@ export class EnterExitHooks {
1515
}
1616

1717
registerOnEnterHooks() {
18-
this.transition.entering().filter(state => !!state.onEnter)
19-
.forEach(state => this.transition.onEnter({entering: state.name}, state.onEnter));
18+
this.transition.entering().map(x => x.$$state()).filter(state => !!state.onEnter)
19+
.forEach(state =>
20+
this.transition.onEnter({entering: state.name}, state.onEnter));
2021
}
2122

2223
registerOnRetainHooks() {
23-
this.transition.retained().filter(state => !!state.onRetain)
24-
.forEach(state => this.transition.onRetain({retained: state.name}, state.onRetain));
24+
this.transition.retained().map(x => x.$$state()).filter(state => !!state.onRetain)
25+
.forEach(state =>
26+
this.transition.onRetain({retained: state.name}, state.onRetain));
2527
}
2628

2729
registerOnExitHooks() {
28-
this.transition.exiting().filter(state => !!state.onExit)
29-
.forEach(state => this.transition.onExit({exiting: state.name}, state.onExit));
30+
this.transition.exiting().map(x => x.$$state()).filter(state => !!state.onExit)
31+
.forEach(state =>
32+
this.transition.onExit({exiting: state.name}, state.onExit));
3033
}
3134
}

src/state/hooks/resolveHooks.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {ResolvePolicy} from "../../resolve/interface";
77
import {Transition} from "../../transition/transition";
88
import {val} from "../../common/hof";
99
import {Resolvable} from "../../resolve/resolvable";
10+
import {State} from "../stateObject";
1011

1112

1213
let LAZY = ResolvePolicy[ResolvePolicy.LAZY];
@@ -25,23 +26,22 @@ export class ResolveHooks {
2526
let treeChanges = this.transition.treeChanges();
2627

2728
/** a function which resolves any EAGER Resolvables for a Path */
28-
(<any> $eagerResolvePath).$inject = ['$transition$'];
29-
function $eagerResolvePath($transition$) {
29+
function $eagerResolvePath($transition$: Transition) {
3030
return tail(<any[]> treeChanges.to).resolveContext.resolvePath(extend({ transition: $transition$ }, { resolvePolicy: EAGER }));
3131
}
3232

3333
/** Returns a function which pre-resolves any LAZY Resolvables for a Node in a Path */
34-
(<any> $lazyResolveEnteringState).$inject = ['$state$', '$transition$'];
35-
function $lazyResolveEnteringState($state$, $transition$) {
36-
let node = find(<any[]> treeChanges.entering, propEq('state', $state$));
34+
function $lazyResolveEnteringState(transition: Transition, injector, state: State) {
35+
let node = find(<any[]> treeChanges.entering, propEq('state', state));
3736

3837
// A new Resolvable contains all the resolved data in this context as a single object, for injection as `$resolve$`
3938
let context = node.resolveContext;
4039
let $resolve$ = new Resolvable("$resolve$", () => map(context.getResolvables(), (r: Resolvable) => r.data));
41-
var options = extend({transition: $transition$}, { resolvePolicy: LAZY });
40+
var options = extend({ transition: transition }, { resolvePolicy: LAZY });
4241

4342
// Resolve all the LAZY resolves, then resolve the `$resolve$` object, then add `$resolve$` to the context
44-
return context.resolvePathElement(node.state, options)
43+
// return context.resolvePathElement(node.state, options)
44+
return context.resolvePath(options)
4545
.then(() => $resolve$.resolveResolvable(context))
4646
.then(() => context.addResolvables([$resolve$], node.state));
4747
}

0 commit comments

Comments
 (0)