Skip to content

Commit 2673406

Browse files
refactor(path): Codified criteria match paths and their scopes
refactor(Event): renamed getTransitionEventType to _getEvent feat(transition): Create
1 parent 0dc2c19 commit 2673406

File tree

6 files changed

+135
-96
lines changed

6 files changed

+135
-96
lines changed

src/transition/hookBuilder.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class HookBuilder {
5454
}
5555

5656
buildHooksForPhase(phase: TransitionHookPhase): TransitionHook[] {
57-
return this.$transitions._pluginapi.getTransitionEventTypes(phase)
57+
return this.$transitions._pluginapi._getEvents(phase)
5858
.map(type => this.buildHooks(type))
5959
.reduce(unnestR, [])
6060
.filter(identity);
@@ -78,7 +78,7 @@ export class HookBuilder {
7878
// Fetch the Nodes that caused this hook to match.
7979
let matches: IMatchingNodes = hook.matches(this.treeChanges);
8080
// Select the PathNode[] that will be used as TransitionHook context objects
81-
let matchingNodes: PathNode[] = matches[hookType.criteriaMatchPath];
81+
let matchingNodes: PathNode[] = matches[hookType.criteriaMatchPath.name];
8282

8383
// Return an array of HookTuples
8484
return matchingNodes.map(node => {
@@ -87,7 +87,7 @@ export class HookBuilder {
8787
traceData: { hookType: hookType.name, context: node}
8888
}, this.baseHookOptions);
8989

90-
let state = hookType.hookScope === TransitionHookScope.STATE ? node.state : null;
90+
let state = hookType.criteriaMatchPath.scope === TransitionHookScope.STATE ? node.state : null;
9191
let transitionHook = new TransitionHook(this.transition, state, hook, _options);
9292
return <HookTuple> { hook, node, transitionHook };
9393
});

src/transition/hookRegistry.ts

+26-23
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
/** @coreapi @module transition */ /** for typedoc */
2-
import { extend, removeFrom, allTrueR, tail, uniqR, pushTo, equals, values, identity } from "../common/common";
2+
import { extend, removeFrom, tail, values, identity, map } from "../common/common";
33
import {isString, isFunction} from "../common/predicates";
44
import {PathNode} from "../path/node";
55
import {
66
TransitionStateHookFn, TransitionHookFn, TransitionHookPhase, TransitionHookScope, IHookRegistry
77
} from "./interface"; // has or is using
88

99
import {
10-
HookRegOptions, HookMatchCriteria, IHookRegistration, TreeChanges,
10+
HookRegOptions, HookMatchCriteria, TreeChanges,
1111
HookMatchCriterion, IMatchingNodes, HookFn
1212
} from "./interface";
1313
import {Glob} from "../common/glob";
@@ -51,7 +51,7 @@ export function matchState(state: State, criterion: HookMatchCriterion) {
5151
* @hidden
5252
* The registration data for a registered transition hook
5353
*/
54-
export class RegisteredHook implements RegisteredHook {
54+
export class RegisteredHook {
5555
matchCriteria: HookMatchCriteria;
5656
priority: number;
5757
bind: any;
@@ -93,21 +93,7 @@ export class RegisteredHook implements RegisteredHook {
9393
* { to: true, from: true, entering: true, exiting: true, retained: true }
9494
*/
9595
private _getDefaultMatchCriteria(): HookMatchCriteria {
96-
return this.tranSvc._pluginapi.getTransitionEventTypes()
97-
.map(type => type.criteriaMatchPath)
98-
.reduce<any[]>(uniqR, [])
99-
.reduce((acc, path) => (acc[path] = true, acc), {});
100-
}
101-
102-
/**
103-
* For all the criteria match paths in all TransitionHookTypes,
104-
* return an object where: keys are pathname, vals are TransitionHookScope
105-
*/
106-
private _getPathScopes(): { [key: string]: TransitionHookScope } {
107-
return this.tranSvc._pluginapi.getTransitionEventTypes().reduce((paths, type) => {
108-
paths[type.criteriaMatchPath] = type.hookScope;
109-
return paths
110-
}, {});
96+
return map(this.tranSvc._pluginapi._getPaths(), () => true);
11197
}
11298

11399
/**
@@ -122,15 +108,15 @@ export class RegisteredHook implements RegisteredHook {
122108
* };
123109
*/
124110
private _getMatchingNodes(treeChanges: TreeChanges): IMatchingNodes {
125-
let pathScopes: { [key: string]: TransitionHookScope } = this._getPathScopes();
111+
let paths: PathType[] = values(this.tranSvc._pluginapi._getPaths());
126112

127-
return Object.keys(pathScopes).reduce((mn: IMatchingNodes, pathName: string) => {
113+
return paths.reduce((mn: IMatchingNodes, path: PathType) => {
128114
// STATE scope criteria matches against every node in the path.
129115
// TRANSITION scope criteria matches against only the last node in the path
130-
let isStateHook = pathScopes[pathName] === TransitionHookScope.STATE;
131-
let nodes: PathNode[] = isStateHook ? treeChanges[pathName] : [tail(treeChanges[pathName])];
116+
let name = path.name, isStateHook = path.scope === TransitionHookScope.STATE;
117+
let nodes: PathNode[] = isStateHook ? treeChanges[name] : [tail(treeChanges[name])];
132118

133-
mn[pathName] = this._matchingNodes(nodes, this.matchCriteria[pathName]);
119+
mn[name] = this._matchingNodes(nodes, this.matchCriteria[name]);
134120
return mn;
135121
}, {} as IMatchingNodes);
136122
}
@@ -155,6 +141,23 @@ export interface RegisteredHooks {
155141
[key: string]: RegisteredHook[];
156142
}
157143

144+
/** @hidden */
145+
export interface PathTypes {
146+
[key: string]: PathType
147+
148+
to: PathType
149+
from: PathType
150+
exiting: PathType
151+
retained: PathType
152+
entering: PathType
153+
}
154+
155+
/** @hidden */
156+
export interface PathType {
157+
name: string;
158+
scope: TransitionHookScope;
159+
}
160+
158161
/** @hidden Return a registration function of the requested type. */
159162
export function makeEvent(registry: IHookRegistry, transitionService: TransitionService, eventType: TransitionEventType) {
160163
// Create the object which holds the registered transition hooks.

src/transition/transition.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export class Transition implements IHookRegistry {
116116
* (which can then be used to register hooks)
117117
*/
118118
private createTransitionHookRegFns() {
119-
this.router.transitionService._pluginapi.getTransitionEventTypes()
119+
this.router.transitionService._pluginapi._getEvents()
120120
.filter(type => type.hookPhase !== TransitionHookPhase.CREATE)
121121
.forEach(type => makeEvent(this, this.router.transitionService, type));
122122
}

src/transition/transitionEventType.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import {TransitionHookScope, TransitionHookPhase} from "./interface";
2-
import {PathNode} from "../path/node";
3-
import {Transition} from "./transition";
4-
import {isString} from "../common/predicates";
5-
import {GetErrorHandler, GetResultHandler, TransitionHook} from "./transitionHook";
1+
import { TransitionHookPhase } from "./interface";
2+
import { GetErrorHandler, GetResultHandler, TransitionHook } from "./transitionHook";
3+
import { PathType } from "./hookRegistry";
64
/**
75
* This class defines a type of hook, such as `onBefore` or `onEnter`.
86
* Plugins can define custom hook types, such as sticky states does for `onInactive`.
@@ -14,9 +12,8 @@ export class TransitionEventType {
1412

1513
constructor(public name: string,
1614
public hookPhase: TransitionHookPhase,
17-
public hookScope: TransitionHookScope,
1815
public hookOrder: number,
19-
public criteriaMatchPath: string,
16+
public criteriaMatchPath: PathType,
2017
public reverseSort: boolean = false,
2118
public getResultHandler: GetResultHandler = TransitionHook.HANDLE_RESULT,
2219
public getErrorHandler: GetErrorHandler = TransitionHook.REJECT_ERROR,

src/transition/transitionService.ts

+100-61
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,25 @@
11
/** @coreapi @module transition */ /** for typedoc */
22
import {
3-
IHookRegistry, TransitionOptions, TransitionHookScope, TransitionHookPhase,
4-
TransitionCreateHookFn
3+
IHookRegistry, TransitionOptions, TransitionHookScope, TransitionHookPhase, TransitionCreateHookFn,
4+
HookMatchCriteria, HookRegOptions
55
} from "./interface";
6-
7-
import {
8-
HookMatchCriteria, HookRegOptions, TransitionStateHookFn, TransitionHookFn
9-
} from "./interface"; // has or is using
10-
11-
import {Transition} from "./transition";
12-
import {RegisteredHooks, makeEvent, RegisteredHook} from "./hookRegistry";
13-
import {TargetState} from "../state/targetState";
14-
import {PathNode} from "../path/node";
15-
import {ViewService} from "../view/view";
16-
import {UIRouter} from "../router";
17-
18-
import {registerEagerResolvePath, registerLazyResolveState} from "../hooks/resolve";
19-
import {registerLoadEnteringViews, registerActivateViews} from "../hooks/views";
20-
import {registerUpdateUrl} from "../hooks/url";
21-
import {registerRedirectToHook} from "../hooks/redirectTo";
22-
import {registerOnExitHook, registerOnRetainHook, registerOnEnterHook} from "../hooks/onEnterExitRetain";
23-
import {registerLazyLoadHook} from "../hooks/lazyLoad";
24-
import {TransitionEventType} from "./transitionEventType";
6+
import { Transition } from "./transition";
7+
import { RegisteredHooks, makeEvent, RegisteredHook, PathTypes, PathType } from "./hookRegistry";
8+
import { TargetState } from "../state/targetState";
9+
import { PathNode } from "../path/node";
10+
import { ViewService } from "../view/view";
11+
import { UIRouter } from "../router";
12+
import { registerEagerResolvePath, registerLazyResolveState } from "../hooks/resolve";
13+
import { registerLoadEnteringViews, registerActivateViews } from "../hooks/views";
14+
import { registerUpdateUrl } from "../hooks/url";
15+
import { registerRedirectToHook } from "../hooks/redirectTo";
16+
import { registerOnExitHook, registerOnRetainHook, registerOnEnterHook } from "../hooks/onEnterExitRetain";
17+
import { registerLazyLoadHook } from "../hooks/lazyLoad";
18+
import { TransitionEventType } from "./transitionEventType";
2519
import { TransitionHook, GetResultHandler, GetErrorHandler } from "./transitionHook";
26-
import {isDefined} from "../common/predicates";
20+
import { isDefined } from "../common/predicates";
2721
import { removeFrom, values, bindFunctions } from "../common/common";
28-
import { Disposable } from "../interface";
22+
import { Disposable } from "../interface"; // has or is using
2923

3024
/**
3125
* The default [[Transition]] options.
@@ -45,6 +39,15 @@ export let defaultTransOpts: TransitionOptions = {
4539
source : "unknown"
4640
};
4741

42+
43+
export interface TransitionServicePluginAPI {
44+
_definePath(name: string, hookScope: TransitionHookScope);
45+
_getPaths(): PathTypes;
46+
_defineEvent(hookType: TransitionEventType): void;
47+
_getEvents(phase?: TransitionHookPhase): TransitionEventType[];
48+
getHooks(hookName: string): RegisteredHook[];
49+
}
50+
4851
/**
4952
* This class provides services related to Transitions.
5053
*
@@ -104,7 +107,17 @@ export class TransitionService implements IHookRegistry, Disposable {
104107
/** @hidden The transition hook types, such as `onEnter`, `onStart`, etc */
105108
private _eventTypes: TransitionEventType[] = [];
106109
/** @hidden The registered transition hooks */
107-
_registeredHooks: RegisteredHooks = { };
110+
_registeredHooks = { } as RegisteredHooks;
111+
/** @hidden The paths on a criteria object */
112+
private _criteriaPaths = { } as PathTypes;
113+
114+
_pluginapi = <TransitionServicePluginAPI> bindFunctions(this, {}, this, [
115+
'_definePath',
116+
'_defineEvent',
117+
'_getPaths',
118+
'_getEvents',
119+
'getHooks',
120+
]);
108121

109122
/**
110123
* This object has hook de-registration functions for the built-in hooks.
@@ -128,8 +141,11 @@ export class TransitionService implements IHookRegistry, Disposable {
128141
constructor(private _router: UIRouter) {
129142
this.$view = _router.viewService;
130143
this._deregisterHookFns = <any> {};
131-
this.registerTransitionHookTypes();
132-
this.registerTransitionHooks();
144+
145+
this._defineDefaultPaths();
146+
this._defineDefaultEvents();
147+
148+
this._registerDefaultTransitionHooks();
133149
}
134150

135151
/** @internalapi */
@@ -157,59 +173,62 @@ export class TransitionService implements IHookRegistry, Disposable {
157173
}
158174

159175
/** @hidden */
160-
private registerTransitionHookTypes() {
161-
const Scope = TransitionHookScope;
176+
private _defineDefaultEvents() {
162177
const Phase = TransitionHookPhase;
163178
const TH = TransitionHook;
179+
const paths = this._criteriaPaths;
164180

165-
this.defineEvent("onCreate", Phase.CREATE, Scope.TRANSITION, 0, "to", false, TH.IGNORE_RESULT, TH.THROW_ERROR, false);
181+
this._defineEvent("onCreate", Phase.CREATE, 0, paths.to, false, TH.IGNORE_RESULT, TH.THROW_ERROR, false);
166182

167-
this.defineEvent("onBefore", Phase.BEFORE, Scope.TRANSITION, 0, "to", false, TH.HANDLE_RESULT);
183+
this._defineEvent("onBefore", Phase.BEFORE, 0, paths.to, false, TH.HANDLE_RESULT);
168184

169-
this.defineEvent("onStart", Phase.ASYNC, Scope.TRANSITION, 0, "to");
170-
this.defineEvent("onExit", Phase.ASYNC, Scope.STATE, 100, "exiting", true);
171-
this.defineEvent("onRetain", Phase.ASYNC, Scope.STATE, 200, "retained");
172-
this.defineEvent("onEnter", Phase.ASYNC, Scope.STATE, 300, "entering");
173-
this.defineEvent("onFinish", Phase.ASYNC, Scope.TRANSITION, 400, "to");
185+
this._defineEvent("onStart", Phase.ASYNC, 0, paths.to);
186+
this._defineEvent("onExit", Phase.ASYNC, 100, paths.exiting, true);
187+
this._defineEvent("onRetain", Phase.ASYNC, 200, paths.retained);
188+
this._defineEvent("onEnter", Phase.ASYNC, 300, paths.entering);
189+
this._defineEvent("onFinish", Phase.ASYNC, 400, paths.to);
174190

175-
this.defineEvent("onSuccess", Phase.SUCCESS, Scope.TRANSITION, 0, "to", false, TH.IGNORE_RESULT, TH.LOG_ERROR, false);
176-
this.defineEvent("onError", Phase.ERROR, Scope.TRANSITION, 0, "to", false, TH.IGNORE_RESULT, TH.LOG_ERROR, false);
191+
this._defineEvent("onSuccess", Phase.SUCCESS, 0, paths.to, false, TH.IGNORE_RESULT, TH.LOG_ERROR, false);
192+
this._defineEvent("onError", Phase.ERROR, 0, paths.to, false, TH.IGNORE_RESULT, TH.LOG_ERROR, false);
177193
}
178194

179-
_pluginapi = <TransitionServicePluginAPI> bindFunctions(this, {}, this, [
180-
'registerTransitionHookType',
181-
'getTransitionEventTypes',
182-
'getHooks',
183-
]);
195+
/** @hidden */
196+
private _defineDefaultPaths() {
197+
const { STATE, TRANSITION } = TransitionHookScope;
198+
199+
this._definePath("to", TRANSITION);
200+
this._definePath("from", TRANSITION);
201+
this._definePath("exiting", STATE);
202+
this._definePath("retained", STATE);
203+
this._definePath("entering", STATE);
204+
}
184205

185206
/**
186207
* Defines a transition hook type and returns a transition hook registration
187208
* function (which can then be used to register hooks of this type).
188209
* @internalapi
189210
*/
190-
defineEvent(name: string,
191-
hookPhase: TransitionHookPhase,
192-
hookScope: TransitionHookScope,
193-
hookOrder: number,
194-
criteriaMatchPath: string,
195-
reverseSort: boolean = false,
196-
getResultHandler: GetResultHandler = TransitionHook.HANDLE_RESULT,
197-
getErrorHandler: GetErrorHandler = TransitionHook.REJECT_ERROR,
198-
rejectIfSuperseded: boolean = true)
211+
_defineEvent(name: string,
212+
hookPhase: TransitionHookPhase,
213+
hookOrder: number,
214+
criteriaMatchPath: PathType,
215+
reverseSort: boolean = false,
216+
getResultHandler: GetResultHandler = TransitionHook.HANDLE_RESULT,
217+
getErrorHandler: GetErrorHandler = TransitionHook.REJECT_ERROR,
218+
rejectIfSuperseded: boolean = true)
199219
{
200-
let eventType = new TransitionEventType(name, hookPhase, hookScope, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, rejectIfSuperseded);
220+
let eventType = new TransitionEventType(name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, rejectIfSuperseded);
201221

202222
this._eventTypes.push(eventType);
203223
makeEvent(this, this, eventType);
204224
};
205225

206-
207226
/**
208227
* @hidden
209228
* Returns the known event types, such as `onBefore`
210229
* If a phase argument is provided, returns only events for the given phase.
211230
*/
212-
private getTransitionEventTypes(phase?: TransitionHookPhase): TransitionEventType[] {
231+
private _getEvents(phase?: TransitionHookPhase): TransitionEventType[] {
213232
let transitionHookTypes = isDefined(phase) ?
214233
this._eventTypes.filter(type => type.hookPhase === phase) :
215234
this._eventTypes.slice();
@@ -220,13 +239,39 @@ export class TransitionService implements IHookRegistry, Disposable {
220239
})
221240
}
222241

242+
/**
243+
* Adds a Path to be used as a criterion against a TreeChanges path
244+
*
245+
* For example: the `exiting` path in [[HookMatchCriteria]] is a STATE scoped path.
246+
* It was defined by calling `defineTreeChangesCriterion('exiting', TransitionHookScope.STATE)`
247+
* Each state in the exiting path is checked against the criteria and returned as part of the match.
248+
*
249+
* Another example: the `to` path in [[HookMatchCriteria]] is a TRANSITION scoped path.
250+
* It was defined by calling `defineTreeChangesCriterion('to', TransitionHookScope.TRANSITION)`
251+
* Only the tail of the `to` path is checked against the criteria and returned as part of the match.
252+
*
253+
* @internalapi
254+
*/
255+
private _definePath(name: string, hookScope: TransitionHookScope) {
256+
this._criteriaPaths[name] = { name, scope: hookScope };
257+
}
258+
259+
/**
260+
* Gets a Path definition used as a criterion against a TreeChanges path
261+
*
262+
* @internalapi
263+
*/
264+
private _getPaths(): PathTypes {
265+
return this._criteriaPaths;
266+
}
267+
223268
/** @hidden */
224269
public getHooks(hookName: string): RegisteredHook[] {
225270
return this._registeredHooks[hookName];
226271
}
227272

228273
/** @hidden */
229-
private registerTransitionHooks() {
274+
private _registerDefaultTransitionHooks() {
230275
let fns = this._deregisterHookFns;
231276

232277
// Wire up redirectTo hook
@@ -252,9 +297,3 @@ export class TransitionService implements IHookRegistry, Disposable {
252297
fns.lazyLoad = registerLazyLoadHook(this);
253298
}
254299
}
255-
256-
export interface TransitionServicePluginAPI {
257-
registerTransitionHookType(hookType: TransitionEventType): void;
258-
getTransitionEventTypes(phase?: TransitionHookPhase): TransitionEventType[];
259-
getHooks(hookName: string): RegisteredHook[];
260-
}

0 commit comments

Comments
 (0)