From 964caaf77415a8b61ef43d216055c6bebbc88a58 Mon Sep 17 00:00:00 2001 From: christopherthielen Date: Sat, 31 Jan 2015 19:53:03 -0600 Subject: [PATCH] fix(dsr): Allow default substates and parameters for deep state redirect. Closes #150 --- src/dsr.js | 33 +++++++++++++++++++++++---------- test/dsrSpec.js | 31 ++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/dsr.js b/src/dsr.js index a8e8a2c..b230d58 100644 --- a/src/dsr.js +++ b/src/dsr.js @@ -43,14 +43,19 @@ angular.module('ct.ui.router.extras.dsr').service("$deepStateRedirect", [ '$root } function getConfig(state) { - var declaration = state.deepStateRedirect; + var declaration = state.deepStateRedirect || state.dsr; if (!declaration) return { dsr: false }; var dsrCfg = { dsr: true }; - if (angular.isFunction(declaration)) + if (angular.isFunction(declaration)) { dsrCfg.fn = declaration; - else if (angular.isObject(declaration)) + } else if (angular.isObject(declaration)) { dsrCfg = angular.extend(dsrCfg, declaration); + } + + if (angular.isString(dsrCfg.default)) { + dsrCfg.default = { state: dsrCfg.default }; + } if (!dsrCfg.fn) { dsrCfg.fn = [ '$dsr$', function($dsr$) { @@ -80,23 +85,30 @@ angular.module('ct.ui.router.extras.dsr').service("$deepStateRedirect", [ '$root return deepStateRedirectsByName[state.name] || false; } - function getParamsString(params, dsrParams) { - function safeString(input) { return !input ? input : input.toString(); } + function getMatchParams(params, dsrParams) { if (dsrParams === true) dsrParams = Object.keys(params); if (dsrParams === null || dsrParams === undefined) dsrParams = []; + var matchParams = {}; + angular.forEach(dsrParams.sort(), function(name) { matchParams[name] = params[name]; }); + return matchParams; + } + + function getParamsString(params, dsrParams) { + var matchParams = getMatchParams(params, dsrParams); + function safeString(input) { return !input ? input : input.toString(); } var paramsToString = {}; - angular.forEach(dsrParams.sort(), function(name) { paramsToString[name] = safeString(params[name]); }); + angular.forEach(matchParams, function(val, name) { paramsToString[name] = safeString(val); }); return angular.toJson(paramsToString); } $rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) { - if (ignoreDsr || computeDeepStateStatus(toState) !== REDIRECT) return; + var cfg = getConfig(toState); + if (ignoreDsr || (computeDeepStateStatus(toState) !== REDIRECT) && !cfg.default) return; // We're changing directly to one of the redirect (tab) states. // Get the DSR key for this state by calculating the DSRParams option - var cfg = getConfig(toState); var key = getParamsString(toParams, cfg.params); - var redirect = lastSubstate[toState.name][key]; + var redirect = lastSubstate[toState.name][key] || cfg.default; if (!redirect) return; // we have a last substate recorded @@ -105,7 +117,8 @@ angular.module('ct.ui.router.extras.dsr').service("$deepStateRedirect", [ '$root if (!result) return; if (result.state) redirect = result; event.preventDefault(); - $state.go(redirect.state, redirect.params); + var redirectParams = getMatchParams(toParams, cfg.params); + $state.go(redirect.state, angular.extend(redirectParams, redirect.params)); }); $rootScope.$on("$stateChangeSuccess", function (event, toState, toParams, fromState, fromParams) { diff --git a/test/dsrSpec.js b/test/dsrSpec.js index 1fff7f9..8e5bae5 100644 --- a/test/dsrSpec.js +++ b/test/dsrSpec.js @@ -16,7 +16,16 @@ function getDSRStates () { { name: 'p2', url: '/p2/:param1/:param2', deepStateRedirect: { params: true } }, { name: 'p2.child' }, { name: 'p3', url: '/p3/:param1', deepStateRedirect: { params: true } }, - { name: 'p3.child'} + { name: 'p3.child'}, + { name: 'p4', url: '/p4', dsr: { default: "p4.child" } }, + { name: 'p4.child'}, + { name: 'p4.child2'}, + { name: 'p5', url: '/p5', dsr: { default: { state: "p5.child", params: { p5param: "1" } } } }, + { name: 'p5.child', url: '/child/:p5param'}, + { name: 'p6', url: '/p6/:param', dsr: { params: true, default: "p6.child1" } }, + { name: 'p6.child1'}, + { name: 'p6.child2'}, + { name: 'p6.child3'} ]; } @@ -155,4 +164,24 @@ describe('deepStateRedirect', function () { expect($state.current.name).toBe("tabs.tabs1"); }); }); + + describe("default substates", function() { + it("should affect the first transition to the DSR state", function() { + testGo("p4", undefined, { redirect: 'p4.child'}); + testGo("p4.child2"); + testGo("p4", undefined, { redirect: 'p4.child2'}); + }); + + it("should provide default parameters", function() { + testGo("p5", undefined, { redirect: 'p5.child'}); + expect($state.params).toEqual({p5param: "1"}); + }); + + it("should redirect to the default state when params: true and transition to DSR with un-seen param values", function() { + testGo("p6", undefined, { params: {param: "1"}, redirect: 'p6.child1'}); + testGo("p6.child2"); + testGo("p6", undefined, { params: {param: "1"}, redirect: 'p6.child2'}); + testGo("p6", undefined, { params: {param: "2"}, redirect: 'p6.child1'}); + }); + }) });