From 0cdf20df376e67740ecda8bd6e2b449247aa2a7c Mon Sep 17 00:00:00 2001 From: weingaro Date: Wed, 4 Mar 2015 17:04:52 +0200 Subject: [PATCH 1/5] feat($state): added 'state' to state reload method (feat no.1612) - Added reloadState(String) option to $state.transitionTo, which is the name of the state that you wish to reload to. - added 'state' parameter(String) to $state.reload method, to be passed to $state.transitionTo method as an option. - test the $state.reload with state parameter --- src/state.js | 18 ++++++++++--- test/stateSpec.js | 67 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/state.js b/src/state.js index 730e719f8..ea1a9710e 100644 --- a/src/state.js +++ b/src/state.js @@ -823,8 +823,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { * @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: true, inherit: false, notify: true, reloadState: state }); }; /** @@ -938,7 +938,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { $state.transitionTo = function transitionTo(to, toParams, options) { toParams = toParams || {}; options = extend({ - location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false + location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false, reloadState : null }, options || {}); var from = $state.$current, fromParams = $state.params, fromPath = from.path; @@ -982,6 +982,16 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { keep++; state = toPath[keep]; } + } else if (options.reloadState) { + if (!isDefined(findState(options.reloadState))) { + throw new Error("No such state '" + options.reloadState + "'"); + } + + while (state && state === fromPath[keep] && state.toString().toLowerCase() !== options.reloadState.toLowerCase()) { + locals = toLocals[keep] = state.locals; + keep++; + state = toPath[keep]; + } } // If we're going to the same state and all locals are kept, we've got nothing to do. @@ -989,7 +999,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { // 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 (!options.reloadState && shouldTriggerReload(to, from, locals, options)) { if (to.self.reloadOnSearch !== false) $urlRouter.update(); $state.transition = null; return $q.when($state.current); diff --git a/test/stateSpec.js b/test/stateSpec.js index 77c9373c6..4c48f07ee 100644 --- a/test/stateSpec.js +++ b/test/stateSpec.js @@ -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: "
", + controller: function() {log += "logA;"} + }) + .state('logA.logB', { + url: "/logB", + views:{ + '':{ + template: "
", + controller: function() {log += "logB;"} + } + } + }) + .state('logA.logB.logC', { + url: "/logC", + views:{ + '':{ + template: "
", + controller: function() {log += "logC;"} + } + } + }) $stateProvider.state('root.sub2', {url: '/2?param2' }); $provide.value('AppInjectable', AppInjectable); @@ -531,6 +554,45 @@ describe('state', function () { $q.flush(); expect(log).toBe('Success!controller;Success!controller;'); })); + + it('should invoke the controllers by state', inject(function ($state, $q, $timeout, $rootScope, $compile) { + $compile('
')($rootScope); + $state.transitionTo('logA.logB.logC'); + $timeout.flush(); + $q.flush(); + expect(log).toBe('logA;logB;logC;'); + + log = ''; + $state.reload('logA'); + $timeout.flush(); + $q.flush(); + expect(log).toBe('logA;logB;logC;'); + + log = ''; + $state.reload('logA.logB'); + $timeout.flush(); + $q.flush(); + expect(log).toBe('logB;logC;'); + + log = ''; + $state.reload('logA.logB.logC'); + $timeout.flush(); + $q.flush(); + expect(log).toBe('logC;'); + + })); + + it('should throw an exception for invalid reload state', inject(function ($state, $q, $timeout, $rootScope, $compile) { + $compile('
')($rootScope); + $state.transitionTo('logA.logB.logC'); + $timeout.flush(); + $q.flush(); + expect(log).toBe('logA;logB;logC;'); + + expect(function(){ + $state.reload('logInvalid')} + ).toThrow("No such state 'logInvalid'"); + })); }); describe('.is()', function () { @@ -784,6 +846,9 @@ describe('state', function () { 'home.item', 'home.redirect', 'json', + 'logA', + 'logA.logB', + 'logA.logB.logC', 'resolveFail', 'resolveTimeout', 'root', From 9e9c4fa10097b912098d805261ee669fec6338bc Mon Sep 17 00:00:00 2001 From: weingaro Date: Wed, 4 Mar 2015 17:11:15 +0200 Subject: [PATCH 2/5] docs(): Added docs to $state.reload method for 'state' parameter --- src/state.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/state.js b/src/state.js index ea1a9710e..c0983f9bf 100644 --- a/src/state.js +++ b/src/state.js @@ -820,6 +820,28 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { * }); * * + * @param {string} depth A state name which is the root of the resolves to re-resolved. + * @example + *
+     * //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');
+     *   }
+     * });
+     * 
+ * + * `reload()` is just an alias for: + *
+     * $state.transitionTo($state.current, $stateParams, { 
+     *   reload: true, inherit: false, notify: true
+     * });
+     * 
+ * @returns {promise} A promise representing the state of the new transition. See * {@link ui.router.state.$state#methods_go $state.go}. */ @@ -931,6 +953,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { * - **`reload`** (v0.2.5) - {boolean=false}, 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. + * - **'reloadDepth'** - {string=null}, A state name which will be the root state to reload from. + * should be used with options.reload=true * * @returns {promise} A promise representing the state of the new transition. See * {@link ui.router.state.$state#methods_go $state.go}. From 1def04bec373b068e536cb791414f905421e3736 Mon Sep 17 00:00:00 2001 From: weingaro Date: Wed, 4 Mar 2015 17:28:19 +0200 Subject: [PATCH 3/5] docs(): fix parameter name --- src/state.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/state.js b/src/state.js index c0983f9bf..511aea6b3 100644 --- a/src/state.js +++ b/src/state.js @@ -820,7 +820,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { * }); * * - * @param {string} depth A state name which is the root of the resolves to re-resolved. + * @param {string} state A state name which is the root of the resolves to re-resolved. * @example *
      * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item' 
@@ -953,7 +953,7 @@ function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {
      * - **`reload`** (v0.2.5) - {boolean=false}, 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.
-     * - **'reloadDepth'** - {string=null}, A state name which will be the root state to reload from.
+     * - **'reloadState'** - {string=null}, A state name which will be the root state to reload from.
      *    should be used with options.reload=true 
      *
      * @returns {promise} A promise representing the state of the new transition. See

From d463e283790def6d57e49a17457ae654dc74439a Mon Sep 17 00:00:00 2001
From: Maor Yosef 
Date: Wed, 4 Mar 2015 17:43:20 +0200
Subject: [PATCH 4/5] docs(): minor text fix

---
 src/state.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/state.js b/src/state.js
index 511aea6b3..6b98e250b 100644
--- a/src/state.js
+++ b/src/state.js
@@ -829,7 +829,7 @@ function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {
      *
      * app.controller('ctrl', function ($scope, $state) {
      *   $scope.reload = function(){
-     *     //Will reload 'contact.detail' and 'contact.detail.item' states
+     *     //will reload 'contact.detail' and 'contact.detail.item' states
      *     $state.reload('contact.detail');
      *   }
      * });

From 588787fe0a39639953f100a04d78c28f84f2e5ef Mon Sep 17 00:00:00 2001
From: Maor Yosef 
Date: Wed, 4 Mar 2015 18:11:43 +0200
Subject: [PATCH 5/5] tests(): fix, removed unneeded timeout.flush()

---
 test/stateSpec.js | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/test/stateSpec.js b/test/stateSpec.js
index 4c48f07ee..49e36edae 100644
--- a/test/stateSpec.js
+++ b/test/stateSpec.js
@@ -558,25 +558,21 @@ describe('state', function () {
     it('should invoke the controllers by state', inject(function ($state, $q, $timeout, $rootScope, $compile) {
       $compile('
')($rootScope); $state.transitionTo('logA.logB.logC'); - $timeout.flush(); $q.flush(); expect(log).toBe('logA;logB;logC;'); log = ''; $state.reload('logA'); - $timeout.flush(); $q.flush(); expect(log).toBe('logA;logB;logC;'); log = ''; $state.reload('logA.logB'); - $timeout.flush(); $q.flush(); expect(log).toBe('logB;logC;'); log = ''; $state.reload('logA.logB.logC'); - $timeout.flush(); $q.flush(); expect(log).toBe('logC;'); @@ -585,7 +581,6 @@ describe('state', function () { it('should throw an exception for invalid reload state', inject(function ($state, $q, $timeout, $rootScope, $compile) { $compile('
')($rootScope); $state.transitionTo('logA.logB.logC'); - $timeout.flush(); $q.flush(); expect(log).toBe('logA;logB;logC;');