diff --git a/src/state.js b/src/state.js index b5b56da9c..7fdc98ddc 100644 --- a/src/state.js +++ b/src/state.js @@ -793,6 +793,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { } if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'"); if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState); + var defaultParams = {}; + forEach(toState.path, function(state) { extend(defaultParams, state && state.self && state.self.params); }); + toParams = extend(defaultParams, toParams); to = toState; var toPath = to.path; diff --git a/test/stateSpec.js b/test/stateSpec.js index 7dbbfff5a..ed7c3a3a0 100644 --- a/test/stateSpec.js +++ b/test/stateSpec.js @@ -20,13 +20,14 @@ describe('state', function () { var A = { data: {} }, B = {}, C = {}, - D = { params: { x: {}, y: {} } }, - DD = { parent: D, params: { x: {}, y: {}, z: {} } }, + D = { params: { x: null, y: null } }, + DD = { parent: D, params: { x: null, y: null, z: null } }, E = { params: { i: {} } }, H = { data: {propA: 'propA', propB: 'propB'} }, HH = { parent: H }, HHH = {parent: HH, data: {propA: 'overriddenA', propC: 'propC'} }, RS = { url: '^/search?term', reloadOnSearch: false }, + OPT = { url: '/opt/:param', params: { param: 100 } }, AppInjectable = {}; beforeEach(module(function ($stateProvider, $provide) { @@ -46,6 +47,7 @@ describe('state', function () { .state('H', H) .state('HH', HH) .state('HHH', HHH) + .state('OPT', OPT) .state('RS', RS) .state('home', { url: "/" }) @@ -101,8 +103,8 @@ 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.sub2', {url: '/2?param2' }); + .state('root.sub1', {url: '/1?param2' }); + $stateProvider.state('root.sub2', {url: '/2?param2' }); $provide.value('AppInjectable', AppInjectable); })); @@ -642,7 +644,7 @@ describe('state', function () { it('contains the parameter values for the current state', inject(function ($state, $q) { initStateTo(D, { x: 'x value', z: 'invalid value' }); - expect($state.params).toEqual({ x: 'x value', y: undefined }); + expect($state.params).toEqual({ x: 'x value', y: null }); })); }); @@ -745,6 +747,7 @@ describe('state', function () { 'H', 'HH', 'HHH', + 'OPT', 'RS', 'about', 'about.person', @@ -786,6 +789,26 @@ describe('state', function () { })); }); + describe('optional parameters', function() { + it("should be populated during transition, if unspecified", inject(function($state, $q) { + var stateParams; + $state.get("OPT").onEnter = function($stateParams) { stateParams = $stateParams; }; + $state.go("OPT"); $q.flush(); + expect($state.current.name).toBe("OPT"); + expect($state.params).toEqual({ param: 100 }); + expect(stateParams).toEqual({ param: 100 }); + })); + + it("should be populated during primary transition, if unspecified", inject(function($state, $q) { + var count = 0; + $state.get("OPT").onEnter = function($stateParams) { count++; }; + $state.go("OPT"); $q.flush(); + expect($state.current.name).toBe("OPT"); + expect($state.params).toEqual({ param: 100 }); + expect(count).toEqual(1); + })); + }); + describe('url handling', function () { it('should transition to the same state with different parameters', inject(function ($state, $rootScope, $location) { $location.path("/about/bob");