Skip to content

Commit 3d07567

Browse files
weingaroMaor Yosef
weingaro
authored and
Maor Yosef
committed
feat($state): added 'state' to state reload method (feat no.1612)
- modiefied options.reload parameter in transitionTo method to accept the following: {reload : true} will reload all states {reload : 'foo.bar'} will reload foo.bar, and any children {reload : stateObj} will reload state found in stateObj, and any children. - added 'state' parameter(string|object) to $state.reload method, to be passed to $state.transitionTo method as the reload option. - test the $state.reload with state parameter
1 parent 3e06565 commit 3d07567

File tree

2 files changed

+150
-6
lines changed

2 files changed

+150
-6
lines changed

src/state.js

+41-5
Original file line numberDiff line numberDiff line change
@@ -820,11 +820,33 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
820820
* });
821821
* </pre>
822822
*
823+
* @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved.
824+
* @example
825+
* <pre>
826+
* //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item'
827+
* //and current state is 'contacts.detail.item'
828+
* var app angular.module('app', ['ui.router']);
829+
*
830+
* app.controller('ctrl', function ($scope, $state) {
831+
* $scope.reload = function(){
832+
* //will reload 'contact.detail' and 'contact.detail.item' states
833+
* $state.reload('contact.detail');
834+
* }
835+
* });
836+
* </pre>
837+
*
838+
* `reload()` is just an alias for:
839+
* <pre>
840+
* $state.transitionTo($state.current, $stateParams, {
841+
* reload: true, inherit: false, notify: true
842+
* });
843+
* </pre>
844+
823845
* @returns {promise} A promise representing the state of the new transition. See
824846
* {@link ui.router.state.$state#methods_go $state.go}.
825847
*/
826-
$state.reload = function reload() {
827-
return $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: true });
848+
$state.reload = function reload(state) {
849+
return $state.transitionTo($state.current, $stateParams, { reload: state || true, inherit: false, notify: true});
828850
};
829851

830852
/**
@@ -928,9 +950,11 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
928950
* - **`relative`** - {object=}, When transitioning with relative path (e.g '^'),
929951
* defines which state to be relative from.
930952
* - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
931-
* - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params
953+
* - **`reload`** (v0.2.5) - {boolean=false|string=|object=}, If `true` will force transition even if the state or params
932954
* have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
933955
* use this when you want to force a reload when *everything* is the same, including search params.
956+
* if String, then will reload the state with the name given in reload, and any children.
957+
* if Object, then a stateObj is expected, will reload the state found in stateObj, and any chhildren.
934958
*
935959
* @returns {promise} A promise representing the state of the new transition. See
936960
* {@link ui.router.state.$state#methods_go $state.go}.
@@ -975,21 +999,33 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
975999

9761000
// Starting from the root of the path, keep all levels that haven't changed
9771001
var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
978-
1002+
var skipTriggerReloadCheck = false;
9791003
if (!options.reload) {
9801004
while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) {
9811005
locals = toLocals[keep] = state.locals;
9821006
keep++;
9831007
state = toPath[keep];
9841008
}
1009+
} else if (isString(options.reload) || isObject(options.reload)) {
1010+
skipTriggerReloadCheck = true;
1011+
var stateName = isString(options.reload) ? options.reload : options.reload.name;
1012+
if (!isDefined(findState(stateName))) {
1013+
throw new Error("No such state '" + stateName + "'");
1014+
}
1015+
1016+
while (state && state === fromPath[keep] && state.toString().toLowerCase() !== stateName.toLowerCase()) {
1017+
locals = toLocals[keep] = state.locals;
1018+
keep++;
1019+
state = toPath[keep];
1020+
}
9851021
}
9861022

9871023
// If we're going to the same state and all locals are kept, we've got nothing to do.
9881024
// But clear 'transition', as we still want to cancel any other pending transitions.
9891025
// TODO: We may not want to bump 'transition' if we're called from a location change
9901026
// that we've initiated ourselves, because we might accidentally abort a legitimate
9911027
// transition initiated from code?
992-
if (shouldTriggerReload(to, from, locals, options)) {
1028+
if (!skipTriggerReloadCheck && shouldTriggerReload(to, from, locals, options)) {
9931029
if (to.self.reloadOnSearch !== false) $urlRouter.update();
9941030
$state.transition = null;
9951031
return $q.when($state.current);

test/stateSpec.js

+109-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,30 @@ describe('state', function () {
113113
// State param inheritance tests. param1 is inherited by sub1 & sub2;
114114
// param2 should not be transferred (unless explicitly set).
115115
.state('root', { url: '^/root?param1' })
116-
.state('root.sub1', {url: '/1?param2' });
116+
.state('root.sub1', {url: '/1?param2' })
117+
.state('logA', {
118+
url: "/logA",
119+
template: "<div> <div ui-view/></div>",
120+
controller: function() {log += "logA;"}
121+
})
122+
.state('logA.logB', {
123+
url: "/logB",
124+
views:{
125+
'':{
126+
template: "<div> <div ui-view/></div>",
127+
controller: function() {log += "logB;"}
128+
}
129+
}
130+
})
131+
.state('logA.logB.logC', {
132+
url: "/logC",
133+
views:{
134+
'':{
135+
template: "<div> <div ui-view/></div>",
136+
controller: function() {log += "logC;"}
137+
}
138+
}
139+
})
117140
$stateProvider.state('root.sub2', {url: '/2?param2' });
118141

119142
$provide.value('AppInjectable', AppInjectable);
@@ -531,6 +554,88 @@ describe('state', function () {
531554
$q.flush();
532555
expect(log).toBe('Success!controller;Success!controller;');
533556
}));
557+
558+
it('should invoke the controllers by state when given state name', inject(function ($state, $q, $timeout, $rootScope, $compile) {
559+
$compile('<div> <div ui-view/></div>')($rootScope);
560+
$state.transitionTo('logA.logB.logC');
561+
$q.flush();
562+
expect(log).toBe('logA;logB;logC;');
563+
564+
log = '';
565+
$state.reload('logA');
566+
$q.flush();
567+
expect(log).toBe('logA;logB;logC;');
568+
569+
log = '';
570+
$state.reload('logA.logB');
571+
$q.flush();
572+
expect(log).toBe('logB;logC;');
573+
574+
log = '';
575+
$state.reload('logA.logB.logC');
576+
$q.flush();
577+
expect(log).toBe('logC;');
578+
}));
579+
580+
it('should reload all states when passing false', inject(function ($state, $q, $timeout, $rootScope, $compile) {
581+
$compile('<div> <div ui-view/></div>')($rootScope);
582+
$state.transitionTo('logA.logB.logC');
583+
$q.flush();
584+
expect(log).toBe('logA;logB;logC;');
585+
586+
log = '';
587+
$state.reload(false);
588+
$q.flush();
589+
expect(log).toBe('logA;logB;logC;');
590+
}));
591+
592+
it('should reload all states when passing true', inject(function ($state, $q, $timeout, $rootScope, $compile) {
593+
$compile('<div> <div ui-view/></div>')($rootScope);
594+
$state.transitionTo('logA.logB.logC');
595+
$q.flush();
596+
expect(log).toBe('logA;logB;logC;');
597+
598+
log = '';
599+
$state.reload(true);
600+
$q.flush();
601+
expect(log).toBe('logA;logB;logC;');
602+
}));
603+
604+
605+
it('should invoke the controllers by state when given stateObj', inject(function ($state, $q, $timeout, $rootScope, $compile) {
606+
$compile('<div> <div ui-view/></div>')($rootScope);
607+
$state.transitionTo('logA.logB.logC');
608+
609+
$q.flush();
610+
expect(log).toBe('logA;logB;logC;');
611+
612+
log = '';
613+
$state.reload($state.current);
614+
$q.flush();
615+
expect(log).toBe('logC;');
616+
}));
617+
618+
it('should throw an exception for invalid reload state name', inject(function ($state, $q, $timeout, $rootScope, $compile) {
619+
$compile('<div> <div ui-view/></div>')($rootScope);
620+
$state.transitionTo('logA.logB.logC');
621+
$q.flush();
622+
expect(log).toBe('logA;logB;logC;');
623+
624+
expect(function(){
625+
$state.reload('logInvalid')}
626+
).toThrow("No such state 'logInvalid'");
627+
}));
628+
629+
it('should throw an exception for invalid reload state object', inject(function ($state, $q, $timeout, $rootScope, $compile) {
630+
$compile('<div> <div ui-view/></div>')($rootScope);
631+
$state.transitionTo('logA.logB.logC');
632+
$q.flush();
633+
expect(log).toBe('logA;logB;logC;');
634+
635+
expect(function(){
636+
$state.reload({})}
637+
).toThrow("No such state 'undefined'");
638+
}));
534639
});
535640

536641
describe('.is()', function () {
@@ -784,6 +889,9 @@ describe('state', function () {
784889
'home.item',
785890
'home.redirect',
786891
'json',
892+
'logA',
893+
'logA.logB',
894+
'logA.logB.logC',
787895
'resolveFail',
788896
'resolveTimeout',
789897
'root',

0 commit comments

Comments
 (0)