Skip to content

Commit 232e94b

Browse files
Popescu Danchristopherthielen
Popescu Dan
authored andcommitted
feat($state): is/includes/get work on relative stateOrName
Allow $state.is, .get, and .includes to work on relative stateOrName with a passed-in context for relative lookups.
1 parent c3bb7ad commit 232e94b

File tree

2 files changed

+65
-12
lines changed

2 files changed

+65
-12
lines changed

src/state.js

+26-11
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
137137

138138
if (path) {
139139
if (!base) throw new Error("No reference point given for path '" + name + "'");
140+
base = findState(base);
141+
140142
var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
141143

142144
for (; i < pathLength; i++) {
@@ -973,8 +975,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
973975
*
974976
* @description
975977
* Similar to {@link ui.router.state.$state#methods_includes $state.includes},
976-
* but only checks for the full state name. If params is supplied then it will be
977-
* tested for strict equality against the current active params object, so all params
978+
* but only checks for the full state name. If params is supplied then it will be
979+
* tested for strict equality against the current active params object, so all params
978980
* must match with none missing and no extras.
979981
*
980982
* @example
@@ -990,13 +992,19 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
990992
* <div ng-class="{highlighted: $state.is('.item')}">Item</div>
991993
* </pre>
992994
*
993-
* @param {string|object} stateName The state name (absolute or relative) or state object you'd like to check.
994-
* @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
995+
* @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check.
996+
* @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
995997
* to test against the current active state.
998+
* @param {object=} options An options object. The options are:
999+
*
1000+
* - **`relative`** - {string|object} - If `stateOrName` is a relative state name and `options.relative` is set, .is will
1001+
* test relative to `options.relative` state (or name).
1002+
*
9961003
* @returns {boolean} Returns true if it is the state.
9971004
*/
998-
$state.is = function is(stateOrName, params) {
999-
var state = findState(stateOrName);
1005+
$state.is = function is(stateOrName, params, options) {
1006+
options = extend({ relative: $state.$current }, options || {});
1007+
var state = findState(stateOrName, options.relative);
10001008

10011009
if (!isDefined(state)) {
10021010
return undefined;
@@ -1051,19 +1059,25 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
10511059
*
10521060
* @param {string} stateOrName A partial name, relative name, or glob pattern
10531061
* to be searched for within the current state name.
1054-
* @param {object} params A param object, e.g. `{sectionId: section.id}`,
1062+
* @param {object=} params A param object, e.g. `{sectionId: section.id}`,
10551063
* that you'd like to test against the current active state.
1064+
* @param {object=} options An options object. The options are:
1065+
*
1066+
* - **`relative`** - {string|object=} - If `stateOrName` is a relative state reference and `options.relative` is set,
1067+
* .includes will test relative to `options.relative` state (or name).
1068+
*
10561069
* @returns {boolean} Returns true if it does include the state
10571070
*/
1058-
$state.includes = function includes(stateOrName, params) {
1071+
$state.includes = function includes(stateOrName, params, options) {
1072+
options = extend({ relative: $state.$current }, options || {});
10591073
if (isString(stateOrName) && isGlob(stateOrName)) {
10601074
if (!doesStateMatchGlob(stateOrName)) {
10611075
return false;
10621076
}
10631077
stateOrName = $state.$current.name;
10641078
}
1065-
var state = findState(stateOrName);
10661079

1080+
var state = findState(stateOrName, options.relative);
10671081
if (!isDefined(state)) {
10681082
return undefined;
10691083
}
@@ -1132,13 +1146,14 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
11321146
* @description
11331147
* Returns the state configuration object for any specific state or all states.
11341148
*
1135-
* @param {string|Sbject=} stateOrName (absolute or relative) If provided, will only get the config for
1149+
* @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for
11361150
* the requested state. If not provided, returns an array of ALL state configs.
1151+
* @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context.
11371152
* @returns {Object|Array} State configuration object or array of all objects.
11381153
*/
11391154
$state.get = function (stateOrName, context) {
11401155
if (arguments.length === 0) return objectKeys(states).map(function(name) { return states[name].self; });
1141-
var state = findState(stateOrName, context);
1156+
var state = findState(stateOrName, context || $state.$current);
11421157
return (state && state.self) ? state.self : null;
11431158
};
11441159

test/stateSpec.js

+39-1
Original file line numberDiff line numberDiff line change
@@ -527,10 +527,25 @@ describe('state', function () {
527527

528528
it('should return true when the current state is passed with matching parameters', inject(function ($state, $q) {
529529
$state.transitionTo(D, {x: 'foo', y: 'bar'}); $q.flush();
530+
expect($state.is(D)).toBe(true);
530531
expect($state.is(D, {x: 'foo', y: 'bar'})).toBe(true);
531532
expect($state.is('D', {x: 'foo', y: 'bar'})).toBe(true);
532533
expect($state.is(D, {x: 'bar', y: 'foo'})).toBe(false);
533534
}));
535+
536+
it('should work for relative states', inject(function ($state, $q) {
537+
var options = { relative: $state.get('about') };
538+
539+
$state.transitionTo('about.person'); $q.flush();
540+
expect($state.is('.person', undefined, options)).toBe(true);
541+
542+
$state.transitionTo('about.person', { person: 'bob' }); $q.flush();
543+
expect($state.is('.person', { person: 'bob' }, options)).toBe(true);
544+
expect($state.is('.person', { person: 'john' }, options)).toBe(false);
545+
546+
options.relative = $state.get('about.person.item');
547+
expect($state.is('^', undefined, options)).toBe(true);
548+
}));
534549
});
535550

536551
describe('.includes()', function () {
@@ -580,6 +595,18 @@ describe('state', function () {
580595
expect($state.includes('about.*.*', {person: 'bob'})).toBe(true);
581596
expect($state.includes('about.*.*', {person: 'shawn'})).toBe(false);
582597
}));
598+
599+
it('should work for relative states', inject(function ($state, $q) {
600+
$state.transitionTo('about.person.item', { person: 'bob', id: 5 }); $q.flush();
601+
602+
expect($state.includes('.person', undefined, { relative: 'about' } )).toBe(true);
603+
expect($state.includes('.person', null, { relative: 'about' } )).toBe(true);
604+
605+
expect($state.includes('^', undefined, { relative: $state.get('about.person.item') })).toBe(true);
606+
607+
expect($state.includes('.person', { person: 'bob' }, { relative: $state.get('about') } )).toBe(true);
608+
expect($state.includes('.person', { person: 'steve' }, { relative: $state.get('about') } )).toBe(false);
609+
}));
583610
});
584611

585612
describe('.current', function () {
@@ -595,7 +622,6 @@ describe('state', function () {
595622
}));
596623
});
597624

598-
599625
describe('$current', function () {
600626
it('is always defined', inject(function ($state) {
601627
expect($state.$current).toBeDefined();
@@ -741,6 +767,18 @@ describe('state', function () {
741767
expect(list.map(function(state) { return state.name; })).toEqual(names);
742768
}));
743769

770+
it('should work for relative states', inject(function ($state) {
771+
var about = $state.get('about');
772+
773+
var person = $state.get('.person', about);
774+
expect(person.url).toBe('/:person');
775+
expect($state.get('^', 'about.person').url).toBe('/about');
776+
777+
var item = $state.get('.person.item', about);
778+
expect(item.url).toBe('/:id');
779+
expect($state.get('^.^', item).url).toBe('/about');
780+
}));
781+
744782
it("should return undefined on invalid state query", inject(function ($state) {
745783
expect($state.get(null)).toBeNull();
746784
expect($state.get(false)).toBeNull();

0 commit comments

Comments
 (0)