Skip to content

feat($state): added 'state' to state reload method (feat no.1612) #1809

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 30, 2015
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
50 changes: 46 additions & 4 deletions src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -820,11 +820,33 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
* });
* </pre>
*
* @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved.
* @example
* <pre>
* //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item'
* //and current state is 'contacts.detail.item'
* var app angular.module('app', ['ui.router']);
*
* app.controller('ctrl', function ($scope, $state) {
* $scope.reload = function(){
* //will reload 'contact.detail' and 'contact.detail.item' states
* $state.reload('contact.detail');
* }
* });
* </pre>
*
* `reload()` is just an alias for:
* <pre>
* $state.transitionTo($state.current, $stateParams, {
* reload: true, inherit: false, notify: true
* });
* </pre>

* @returns {promise} A promise representing the state of the new transition. See
* {@link ui.router.state.$state#methods_go $state.go}.
*/
$state.reload = function reload() {
return $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: true });
$state.reload = function reload(state) {
return $state.transitionTo($state.current, $stateParams, { reload: state || true, inherit: false, notify: true});
};

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

// Starting from the root of the path, keep all levels that haven't changed
var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
var skipTriggerReloadCheck = false;

if (!options.reload) {
while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) {
locals = toLocals[keep] = state.locals;
keep++;
state = toPath[keep];
}
} else if (isString(options.reload) || isObject(options.reload)) {
if (isObject(options.reload) && !options.reload.name) {
throw new Error('Invalid reload state object');
}

var reloadState = options.reload === true ? fromPath[0] : findState(options.reload);
if (options.reload && !reloadState) {
throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'");
}

skipTriggerReloadCheck = true;

while (state && state === fromPath[keep] && state !== reloadState) {
locals = toLocals[keep] = state.locals;
keep++;
state = toPath[keep];
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The block above should be something closer to:

var reloadState = options.reload === true ? fromPath[0] : findState(options.state);
 if (options.reload && !reloadState) {
          throw new Error("No such reload state '" + options.reload + "'");
        }
var skipTriggerReloadCheck = !!reloadState;
        while (state && state === fromPath[keep] && state !== reloadState) {
          locals = toLocals[keep] = state.locals;
          keep++;
          state = toPath[keep];
        }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@christopherthielen Do you want this fixed before we merge?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, wait looks like it was already fixed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, they already made the changes I asked for


// If we're going to the same state and all locals are kept, we've got nothing to do.
// But clear 'transition', as we still want to cancel any other pending transitions.
// TODO: We may not want to bump 'transition' if we're called from a location change
// that we've initiated ourselves, because we might accidentally abort a legitimate
// transition initiated from code?
if (shouldTriggerReload(to, from, locals, options)) {
if (!skipTriggerReloadCheck && shouldTriggerReload(to, from, locals, options)) {
if (to.self.reloadOnSearch !== false) $urlRouter.update();
$state.transition = null;
return $q.when($state.current);
Expand Down
114 changes: 113 additions & 1 deletion test/stateSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,30 @@ describe('state', function () {
// State param inheritance tests. param1 is inherited by sub1 & sub2;
// param2 should not be transferred (unless explicitly set).
.state('root', { url: '^/root?param1' })
.state('root.sub1', {url: '/1?param2' });
.state('root.sub1', {url: '/1?param2' })
.state('logA', {
url: "/logA",
template: "<div> <div ui-view/></div>",
controller: function() {log += "logA;"}
})
.state('logA.logB', {
url: "/logB",
views:{
'':{
template: "<div> <div ui-view/></div>",
controller: function() {log += "logB;"}
}
}
})
.state('logA.logB.logC', {
url: "/logC",
views:{
'':{
template: "<div> <div ui-view/></div>",
controller: function() {log += "logC;"}
}
}
})
$stateProvider.state('root.sub2', {url: '/2?param2' });

$provide.value('AppInjectable', AppInjectable);
Expand Down Expand Up @@ -531,6 +554,92 @@ describe('state', function () {
$q.flush();
expect(log).toBe('Success!controller;Success!controller;');
}));

it('should invoke the controllers by state when given state name', inject(function ($state, $q, $timeout, $rootScope, $compile) {
$compile('<div> <div ui-view/></div>')($rootScope);
$state.transitionTo('logA.logB.logC');
$q.flush();
expect(log).toBe('logA;logB;logC;');

log = '';
$state.reload('logA');
$q.flush();
expect(log).toBe('logA;logB;logC;');

log = '';
$state.reload('logA.logB');
$q.flush();
expect(log).toBe('logB;logC;');

log = '';
$state.reload('logA.logB.logC');
$q.flush();
expect(log).toBe('logC;');
}));

it('should reload all states when passing false', inject(function ($state, $q, $timeout, $rootScope, $compile) {
$compile('<div> <div ui-view/></div>')($rootScope);
$state.transitionTo('logA.logB.logC');
$q.flush();
expect(log).toBe('logA;logB;logC;');

log = '';
$state.reload(false);
$q.flush();
expect(log).toBe('logA;logB;logC;');
}));

it('should reload all states when passing true', inject(function ($state, $q, $timeout, $rootScope, $compile) {
$compile('<div> <div ui-view/></div>')($rootScope);
$state.transitionTo('logA.logB.logC');
$q.flush();
expect(log).toBe('logA;logB;logC;');

log = '';
$state.reload(true);
$q.flush();
expect(log).toBe('logA;logB;logC;');
}));


it('should invoke the controllers by state when given stateObj', inject(function ($state, $q, $timeout, $rootScope, $compile) {
$compile('<div> <div ui-view/></div>')($rootScope);
$state.transitionTo('logA.logB.logC');

$q.flush();
expect(log).toBe('logA;logB;logC;');

log = '';
$state.reload($state.current);
$q.flush();
expect(log).toBe('logC;');
}));

it('should throw an exception for invalid reload state name', inject(function ($state, $q, $timeout, $rootScope, $compile) {
$compile('<div> <div ui-view/></div>')($rootScope);
$state.transitionTo('logA.logB.logC');
$q.flush();
expect(log).toBe('logA;logB;logC;');

expect(function(){
$state.reload('logInvalid')}
).toThrow("No such reload state 'logInvalid'");
}));

it('should throw an exception for invalid reload state object', inject(function ($state, $q, $timeout, $rootScope, $compile) {
$compile('<div> <div ui-view/></div>')($rootScope);
$state.transitionTo('logA.logB.logC');
$q.flush();
expect(log).toBe('logA;logB;logC;');

expect(function(){
$state.reload({foo:'bar'})}
).toThrow("Invalid reload state object");

expect(function(){
$state.reload({name:'invalidState'})}
).toThrow("No such reload state 'invalidState'");
}));
});

describe('.is()', function () {
Expand Down Expand Up @@ -784,6 +893,9 @@ describe('state', function () {
'home.item',
'home.redirect',
'json',
'logA',
'logA.logB',
'logA.logB.logC',
'resolveFail',
'resolveTimeout',
'root',
Expand Down