diff --git a/src/state/stateObject.ts b/src/state/stateObject.ts index b7848396d..eeac7dbac 100644 --- a/src/state/stateObject.ts +++ b/src/state/stateObject.ts @@ -32,7 +32,7 @@ export class State { public navigable: State; public path: State[]; public data: any; - public includes: (name: string) => boolean; + public includes: { [name: string] : boolean }; constructor(config?: StateDeclaration) { extend(this, config); diff --git a/src/transition/transition.ts b/src/transition/transition.ts index 310cdce71..060a7390c 100644 --- a/src/transition/transition.ts +++ b/src/transition/transition.ts @@ -295,14 +295,16 @@ export class Transition implements IHookRegistry { targetState = new TargetState(targetState.identifier(), targetState.$state(), targetState.params(), newOptions); let redirectTo = new Transition(this._treeChanges.from, targetState, this._transitionService); + let reloadState = targetState.options().reloadState; // If the current transition has already resolved any resolvables which are also in the redirected "to path", then // add those resolvables to the redirected transition. Allows you to define a resolve at a parent level, wait for // the resolve, then redirect to a child state based on the result, and not have to re-fetch the resolve. let redirectedPath = this.treeChanges().to; - let matching: Node[] = Node.matching(redirectTo.treeChanges().to, redirectedPath); + let copyResolvesFor: Node[] = Node.matching(redirectTo.treeChanges().to, redirectedPath) + .filter(node => !reloadState || !reloadState.includes[node.state.name]); const includeResolve = (resolve, key) => ['$stateParams', '$transition$'].indexOf(key) === -1; - matching.forEach((node, idx) => extend(node.resolves, filter(redirectedPath[idx].resolves, includeResolve))); + copyResolvesFor.forEach((node, idx) => extend(node.resolves, filter(redirectedPath[idx].resolves, includeResolve))); return redirectTo; } diff --git a/test/stateSpec.js b/test/stateSpec.js index c83fa0c10..87a02187c 100644 --- a/test/stateSpec.js +++ b/test/stateSpec.js @@ -2095,14 +2095,21 @@ describe('otherwise and state redirects', function() { }); -describe('hook redirects from .otherwise()', function() { - var log; +describe('hook redirects', function() { + var log, resolvelog; beforeEach(module(function ($stateProvider, $urlRouterProvider) { - log = ""; + log = resolvelog = ""; $urlRouterProvider.otherwise('/home'); - $stateProvider - .state('home', { url: '/home', template: 'home', controller: function() { log += "homeCtrl;"; } }) - .state('loginPage', { url: '/login', template: 'login' }); + $stateProvider.state('home', { + url: '/home', + template: 'home ', + controller: function () { log += "homeCtrl;"; }, + resolve: { + foo: function () { resolvelog += "fooResolve;"; return "foo"; } + } + }) + .state('home.foo', {url: '/foo', template: 'foo'}) + .state('loginPage', {url: '/login', template: 'login'}); })); beforeEach(inject(function($compile, $rootScope) { @@ -2111,7 +2118,7 @@ describe('hook redirects from .otherwise()', function() { })); // Test for #2455 - it("should go to the redirect-to target state and url", inject(function($transitions, $q, $state, $location) { + it("from .otherwise() should go to the redirect-to target state and url", inject(function($transitions, $q, $state, $location) { $transitions.onBefore({ to: 'home' }, function() { return $state.target('loginPage', {}, { location: true }); }); @@ -2132,7 +2139,7 @@ describe('hook redirects from .otherwise()', function() { $transitions.onBefore({ to: 'home' }, function($state, $transition$) { var options = $transition$.options(); - if (!options.reload && count++ < 2) { + if (!options.reload && count++ < 5) { return $state.target($transition$.to(), $transition$.params("to"), extend({}, options, {reload: true})); } }); @@ -2142,4 +2149,28 @@ describe('hook redirects from .otherwise()', function() { expect($state.current.name).toBe("home"); expect(log).toBe("homeCtrl;homeCtrl;"); })); + + // Test for #2539 + it("should re-resolve when reloading during a redirect", inject(function($transitions, $q, $state, $trace) { + var count = 0; + $q.flush(); + + expect($state.current.name).toBe("home"); + expect(resolvelog).toBe("fooResolve;"); + + $state.go('home.foo'); $q.flush(); + expect(resolvelog).toBe("fooResolve;"); + + $transitions.onStart({ to: 'home' }, function($transition$, $state) { + if (!$transition$.options().reload && count++ < 5) { + console.log("forcing re-enter (reload) of home state "); + var options = $transition$.options(); + return $state.target($transition$.to(), $transition$.params("to"), extend({}, options, {reload: true})); + } + }); + + $state.go('home'); $q.flush(); + expect($state.current.name).toBe("home"); + expect(resolvelog).toBe("fooResolve;fooResolve;"); + })); });