Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/common/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface TypedMap<T> {
[key: string]: T;
}
export type Predicate<X> = (x?: X) => boolean;
export type PredicateBinary<X, Y> = (x?: X, y?: Y) => boolean;
/**
* An ng1-style injectable
*
Expand Down
12 changes: 8 additions & 4 deletions src/transition/hookBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class HookBuilder {
const treeChanges = transition.treeChanges();

// Find all the matching registered hooks for a given hook type
const matchingHooks = this.getMatchingHooks(hookType, treeChanges);
const matchingHooks = this.getMatchingHooks(hookType, treeChanges, transition);
if (!matchingHooks) return [];

const baseHookOptions = <TransitionHookOptions>{
Expand All @@ -70,7 +70,7 @@ export class HookBuilder {

const makeTransitionHooks = (hook: RegisteredHook) => {
// Fetch the Nodes that caused this hook to match.
const matches: IMatchingNodes = hook.matches(treeChanges);
const matches: IMatchingNodes = hook.matches(treeChanges, transition);
// Select the PathNode[] that will be used as TransitionHook context objects
const matchingNodes: PathNode[] = matches[hookType.criteriaMatchPath.name];

Expand Down Expand Up @@ -108,7 +108,11 @@ export class HookBuilder {
*
* @returns an array of matched [[RegisteredHook]]s
*/
public getMatchingHooks(hookType: TransitionEventType, treeChanges: TreeChanges): RegisteredHook[] {
public getMatchingHooks(
hookType: TransitionEventType,
treeChanges: TreeChanges,
transition: Transition
): RegisteredHook[] {
const isCreate = hookType.hookPhase === TransitionHookPhase.CREATE;

// Instance and Global hook registries
Expand All @@ -119,7 +123,7 @@ export class HookBuilder {
.map((reg: IHookRegistry) => reg.getHooks(hookType.name)) // Get named hooks from registries
.filter(assertPredicate(isArray, `broken event named: ${hookType.name}`)) // Sanity check
.reduce(unnestR, []) // Un-nest RegisteredHook[][] to RegisteredHook[] array
.filter(hook => hook.matches(treeChanges)); // Only those satisfying matchCriteria
.filter(hook => hook.matches(treeChanges, transition)); // Only those satisfying matchCriteria
}
}

Expand Down
17 changes: 9 additions & 8 deletions src/transition/hookRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
IMatchingNodes,
HookFn,
} from './interface';
import { Transition } from './transition';
import { StateObject } from '../state/stateObject';
import { TransitionEventType } from './transitionEventType';
import { TransitionService } from './transitionService';
Expand All @@ -35,7 +36,7 @@ import { TransitionService } from './transitionService';
* - If a function, matchState calls the function with the state and returns true if the function's result is truthy.
* @returns {boolean}
*/
export function matchState(state: StateObject, criterion: HookMatchCriterion) {
export function matchState(state: StateObject, criterion: HookMatchCriterion, transition: Transition) {
const toMatch = isString(criterion) ? [criterion] : criterion;

function matchGlobs(_state: StateObject) {
Expand All @@ -51,7 +52,7 @@ export function matchState(state: StateObject, criterion: HookMatchCriterion) {
}

const matchFn = <any>(isFunction(toMatch) ? toMatch : matchGlobs);
return !!matchFn(state);
return !!matchFn(state, transition);
}

/**
Expand Down Expand Up @@ -93,9 +94,9 @@ export class RegisteredHook {
* with `entering: (state) => true` which only matches when a state is actually
* being entered.
*/
private _matchingNodes(nodes: PathNode[], criterion: HookMatchCriterion): PathNode[] {
private _matchingNodes(nodes: PathNode[], criterion: HookMatchCriterion, transition: Transition): PathNode[] {
if (criterion === true) return nodes;
const matching = nodes.filter(node => matchState(node.state, criterion));
const matching = nodes.filter(node => matchState(node.state, criterion, transition));
return matching.length ? matching : null;
}

Expand Down Expand Up @@ -132,7 +133,7 @@ export class RegisteredHook {
* };
* ```
*/
private _getMatchingNodes(treeChanges: TreeChanges): IMatchingNodes {
private _getMatchingNodes(treeChanges: TreeChanges, transition: Transition): IMatchingNodes {
const criteria = extend(this._getDefaultMatchCriteria(), this.matchCriteria);
const paths: PathType[] = values(this.tranSvc._pluginapi._getPathTypes());

Expand All @@ -144,7 +145,7 @@ export class RegisteredHook {
const path = treeChanges[pathtype.name] || [];
const nodes: PathNode[] = isStateHook ? path : [tail(path)];

mn[pathtype.name] = this._matchingNodes(nodes, criteria[pathtype.name]);
mn[pathtype.name] = this._matchingNodes(nodes, criteria[pathtype.name], transition);
return mn;
},
{} as IMatchingNodes
Expand All @@ -157,8 +158,8 @@ export class RegisteredHook {
* @returns an IMatchingNodes object, or null. If an IMatchingNodes object is returned, its values
* are the matching [[PathNode]]s for each [[HookMatchCriterion]] (to, from, exiting, retained, entering)
*/
matches(treeChanges: TreeChanges): IMatchingNodes {
const matches = this._getMatchingNodes(treeChanges);
matches(treeChanges: TreeChanges, transition: Transition): IMatchingNodes {
const matches = this._getMatchingNodes(treeChanges, transition);

// Check if all the criteria matched the TreeChanges object
const allMatched = values(matches).every(identity);
Expand Down
16 changes: 12 additions & 4 deletions src/transition/interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @publicapi @module transition */ /** */
import { StateDeclaration } from '../state/interface';
import { Predicate } from '../common/common';
import { PredicateBinary } from '../common/common';

import { Transition } from './transition';
import { StateObject } from '../state/stateObject';
Expand Down Expand Up @@ -716,8 +716,8 @@ export interface IHookRegistry {
getHooks(hookName: string): RegisteredHook[];
}

/** A predicate type which tests if a [[StateObject]] passes some test. Returns a boolean. */
export type IStateMatch = Predicate<StateObject>;
/** A predicate type which tests if a [[StateObject]] and [[Transition]] passes some test. Returns a boolean. */
export type IStateMatch = PredicateBinary<StateObject, Transition>;

/**
* This object is used to configure whether or not a Transition Hook is invoked for a particular transition,
Expand Down Expand Up @@ -765,6 +765,14 @@ export type IStateMatch = Predicate<StateObject>;
* }
* }
* ```
* #### Example:
* ```js
* // This will match when route is just entered (initial load) or when the state is hard-refreshed
* // by specifying `{refresh: true}` as transition options.
* var match = {
* from: (state, transition) => state.self.name === '' || transition.options().reload
* }
* ```
*
* #### Example:
* ```js
Expand Down Expand Up @@ -826,7 +834,7 @@ export interface PathType {
*
* A [[Glob]] string that matches the name of a state.
*
* Or, a function with the signature `function(state) { return matches; }`
* Or, a function with the signature `function(state, transition) { return matches; }`
* which should return a boolean to indicate if a state matches.
*
* Or, `true` to always match
Expand Down
4 changes: 2 additions & 2 deletions src/transition/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ export class Transition implements IHookRegistry {
return this.is({ to: compare.$to().name, from: compare.$from().name });
}
return !(
(compare.to && !matchState(this.$to(), compare.to)) ||
(compare.from && !matchState(this.$from(), compare.from))
(compare.to && !matchState(this.$to(), compare.to, this)) ||
(compare.from && !matchState(this.$from(), compare.from, this))
);
}

Expand Down