Skip to content

Commit 2d5f6b3

Browse files
committed
feat($state): includes() allows glob patterns for state matching.
1 parent 8ab9778 commit 2d5f6b3

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

src/state.js

+65-2
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,41 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
218218
return state;
219219
}
220220

221+
// Checks text to see if it looks like a glob.
222+
function isGlob (text) {
223+
return text.indexOf('*') > -1;
224+
}
225+
226+
// Returns true if glob matches current $state name.
227+
function doesStateMatchGlob (glob) {
228+
var globSegments = glob.split('.'),
229+
segments = $state.$current.name.split('.');
230+
231+
//match greedy starts
232+
if (globSegments[0] === '**') {
233+
segments = segments.slice(segments.indexOf(globSegments[1]));
234+
segments.unshift('**');
235+
}
236+
//match greedy ends
237+
if (globSegments[globSegments.length - 1] === '**') {
238+
segments.splice(segments.indexOf(globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE);
239+
segments.push('**');
240+
}
241+
242+
if (globSegments.length != segments.length) {
243+
return false;
244+
}
245+
246+
//match single stars
247+
for (var i = 0, l = globSegments.length; i < l; i++) {
248+
if (globSegments[i] === '*') {
249+
segments[i] = '*';
250+
}
251+
}
252+
253+
return segments.join('') === globSegments.join('');
254+
}
255+
221256

222257
// Implicit root state that is always active
223258
root = registerState({
@@ -970,19 +1005,46 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
9701005
*
9711006
* @example
9721007
* <pre>
1008+
* $state.$current.name = 'contacts.details.item';
1009+
*
9731010
* $state.includes("contacts"); // returns true
9741011
* $state.includes("contacts.details"); // returns true
9751012
* $state.includes("contacts.details.item"); // returns true
9761013
* $state.includes("contacts.list"); // returns false
9771014
* $state.includes("about"); // returns false
9781015
* </pre>
9791016
*
980-
* @param {string|object} stateOrName A full or partial state name to be searched for within the current state name.
981-
* @param {object=} params A param object, e.g. `{sectionId: section.id}`,
1017+
* @description
1018+
* Basic globing patterns will also work.
1019+
*
1020+
* @example
1021+
* <pre>
1022+
* $state.$current.name = 'contacts.details.item.url';
1023+
*
1024+
* $state.includes("*.details.*.*"); // returns true
1025+
* $state.includes("*.details.**"); // returns true
1026+
* $state.includes("**.item.**"); // returns true
1027+
* $state.includes("*.details.item.url"); // returns true
1028+
* $state.includes("*.details.*.url"); // returns true
1029+
* $state.includes("*.details.*"); // returns false
1030+
* $state.includes("item.**"); // returns false
1031+
* </pre>
1032+
*
1033+
* @param {string} stateOrName A partial name to be searched for within the current state name.
1034+
* @param {object} params A param object, e.g. `{sectionId: section.id}`,
9821035
* that you'd like to test against the current active state.
9831036
* @returns {boolean} Returns true if it does include the state
9841037
*/
1038+
9851039
$state.includes = function includes(stateOrName, params) {
1040+
if (isString(stateOrName) && isGlob(stateOrName)) {
1041+
if (doesStateMatchGlob(stateOrName)) {
1042+
stateOrName = $state.$current.name;
1043+
} else {
1044+
return false;
1045+
}
1046+
}
1047+
9861048
var state = findState(stateOrName);
9871049
if (!isDefined(state)) {
9881050
return undefined;
@@ -1001,6 +1063,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
10011063
return validParams;
10021064
};
10031065

1066+
10041067
/**
10051068
* @ngdoc function
10061069
* @name ui.router.state.$state#href

test/stateSpec.js

+15
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,21 @@ describe('state', function () {
552552
expect($state.includes('about.person', {person: 'bob'})).toBe(true);
553553
expect($state.includes('about.person', {person: 'steve'})).toBe(false);
554554
}));
555+
556+
it('should return true when the current state is passed with partial glob patterns', inject(function ($state, $q) {
557+
$state.transitionTo('about.person.item', {person: 'bob', id: 5}); $q.flush();
558+
expect($state.includes('*.person.*')).toBe(true);
559+
expect($state.includes('*.person.**')).toBe(true);
560+
expect($state.includes('**.item.*')).toBe(false);
561+
expect($state.includes('**.item')).toBe(true);
562+
expect($state.includes('**.stuff.*')).toBe(false);
563+
expect($state.includes('*.*.*')).toBe(true);
564+
expect($state.includes('about.*.*')).toBe(true);
565+
expect($state.includes('about.**')).toBe(true);
566+
expect($state.includes('*.about.*')).toBe(false);
567+
expect($state.includes('about.*.*', {person: 'bob'})).toBe(true);
568+
expect($state.includes('about.*.*', {person: 'shawn'})).toBe(false);
569+
}));
555570
});
556571

557572
describe('.current', function () {

0 commit comments

Comments
 (0)