aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Minar2011-08-15 08:34:11 -0700
committerIgor Minar2011-08-19 03:55:47 -0700
commitdc0b0c77c7ba3e419442ec639356ab5d3ba7efbe (patch)
treecc277d130745d20b12b7a5cf848203ae9b118d91
parent6114c8f504e99fecb0d62cce4beb3a8f39f301ec (diff)
downloadangular.js-dc0b0c77c7ba3e419442ec639356ab5d3ba7efbe.tar.bz2
feat($route): add reloadOnSearch route param to avoid reloads
In order to avoid unnecesary route reloads when just hashSearch part of the url changes, it is now possible to disable this behavior by setting reloadOnSearch param of the route declaration to false. Closes #354
-rw-r--r--src/service/route.js36
-rw-r--r--test/service/routeSpec.js135
2 files changed, 165 insertions, 6 deletions
diff --git a/src/service/route.js b/src/service/route.js
index 972d616e..b7e4814b 100644
--- a/src/service/route.js
+++ b/src/service/route.js
@@ -68,6 +68,8 @@ angularServiceInject('$route', function($location) {
matcher = switchRouteMatcher,
parentScope = this,
dirty = 0,
+ lastHashPath,
+ lastRouteParams,
$route = {
routes: routes,
@@ -136,6 +138,18 @@ angularServiceInject('$route', function($location) {
* The custom `redirectTo` function is expected to return a string which will be used
* to update `$location.hash`.
*
+ * - `[reloadOnSearch=true]` - {boolean=} - reload route when $location.hashSearch
+ * changes. If this option is disabled, you should set up a $watch to be notified of
+ * param (hashSearch) changes as follows:
+ *
+ * function MyCtrl($route) {
+ * this.$watch(function() {
+ * return $route.current.params;
+ * }, function(scope, params) {
+ * //do stuff with params
+ * });
+ * }
+ *
* @returns {Object} route object
*
* @description
@@ -144,8 +158,8 @@ angularServiceInject('$route', function($location) {
when:function (path, params) {
if (isUndefined(path)) return routes; //TODO(im): remove - not needed!
var route = routes[path];
- if (!route) route = routes[path] = {};
- if (params) extend(route, params);
+ if (!route) route = routes[path] = {reloadOnSearch: true};
+ if (params) extend(route, params); //TODO(im): what the heck? merge two route definitions?
dirty++;
return route;
},
@@ -209,9 +223,18 @@ angularServiceInject('$route', function($location) {
function updateRoute(){
var selectedRoute, pathParams, segmentMatch, key, redir;
- if ($route.current && $route.current.scope) {
- $route.current.scope.$destroy();
+ if ($route.current) {
+ if (!$route.current.reloadOnSearch && (lastHashPath == $location.hashPath)) {
+ $route.current.params = extend($location.hashSearch, lastRouteParams);
+ return;
+ }
+
+ if ($route.current.scope) {
+ $route.current.scope.$destroy();
+ }
}
+
+ lastHashPath = $location.hashPath;
$route.current = null;
// Match a route
forEach(routes, function(rParams, rPath) {
@@ -255,19 +278,20 @@ angularServiceInject('$route', function($location) {
$route.current = extend({}, selectedRoute);
$route.current.params = extend({}, $location.hashSearch, pathParams);
+ lastRouteParams = pathParams;
}
//fire onChange callbacks
forEach(onChange, parentScope.$eval, parentScope);
- // Create the scope if we have mtched a route
+ // Create the scope if we have matched a route
if ($route.current) {
$route.current.scope = parentScope.$new($route.current.controller);
}
}
- this.$watch(function(){return dirty + $location.hash;}, updateRoute);
+ this.$watch(function(){ return dirty + $location.hash; }, updateRoute);
return $route;
}, ['$location']);
diff --git a/test/service/routeSpec.js b/test/service/routeSpec.js
index bfe201d1..72c7745c 100644
--- a/test/service/routeSpec.js
+++ b/test/service/routeSpec.js
@@ -260,4 +260,139 @@ describe('$route', function() {
}
});
});
+
+
+ describe('reloadOnSearch', function() {
+ it('should reload a route when reloadOnSearch is enabled and hashSearch changes', function() {
+ var scope = angular.scope(),
+ $location = scope.$service('$location'),
+ $route = scope.$service('$route'),
+ reloaded = jasmine.createSpy('route reload');
+
+ $route.when('/foo', {controller: FooCtrl});
+ $route.onChange(reloaded);
+
+ function FooCtrl() {
+ reloaded();
+ }
+
+ $location.updateHash('/foo');
+ scope.$digest();
+ expect(reloaded).toHaveBeenCalled();
+ reloaded.reset();
+
+ // trigger reload
+ $location.hashSearch.foo = 'bar';
+ scope.$digest();
+ expect(reloaded).toHaveBeenCalled();
+ });
+
+
+ it('should not reload a route when reloadOnSearch is disabled and only hashSearch changes',
+ function() {
+ var scope = angular.scope(),
+ $location = scope.$service('$location'),
+ $route = scope.$service('$route'),
+ reloaded = jasmine.createSpy('route reload');
+
+ $route.when('/foo', {controller: FooCtrl, reloadOnSearch: false});
+ $route.onChange(reloaded);
+
+ function FooCtrl() {
+ reloaded();
+ }
+
+ expect(reloaded).not.toHaveBeenCalled();
+
+ $location.updateHash('/foo');
+ scope.$digest();
+ expect(reloaded).toHaveBeenCalled();
+ reloaded.reset();
+
+ // don't trigger reload
+ $location.hashSearch.foo = 'bar';
+ scope.$digest();
+ expect(reloaded).not.toHaveBeenCalled();
+ });
+
+
+ it('should reload reloadOnSearch route when url differs only in route path param', function() {
+ var scope = angular.scope(),
+ $location = scope.$service('$location'),
+ $route = scope.$service('$route'),
+ reloaded = jasmine.createSpy('routeReload'),
+ onRouteChange = jasmine.createSpy('onRouteChange');
+
+ $route.when('/foo/:fooId', {controller: FooCtrl, reloadOnSearch: false});
+ $route.onChange(onRouteChange);
+
+ function FooCtrl() {
+ reloaded();
+ }
+
+ expect(reloaded).not.toHaveBeenCalled();
+ expect(onRouteChange).not.toHaveBeenCalled();
+
+ $location.updateHash('/foo/aaa');
+ scope.$digest();
+ expect(reloaded).toHaveBeenCalled();
+ expect(onRouteChange).toHaveBeenCalled();
+ reloaded.reset();
+ onRouteChange.reset();
+
+ $location.updateHash('/foo/bbb');
+ scope.$digest();
+ expect(reloaded).toHaveBeenCalled();
+ expect(onRouteChange).toHaveBeenCalled();
+ reloaded.reset();
+ onRouteChange.reset();
+
+ $location.hashSearch.foo = 'bar';
+ scope.$digest();
+ expect(reloaded).not.toHaveBeenCalled();
+ expect(onRouteChange).not.toHaveBeenCalled();
+ });
+
+
+ it('should update route params when reloadOnSearch is disabled and hashSearch', function() {
+ var scope = angular.scope(),
+ $location = scope.$service('$location'),
+ $route = scope.$service('$route'),
+ routeParams = jasmine.createSpy('routeParams');
+
+ $route.when('/foo', {controller: FooCtrl});
+ $route.when('/bar/:barId', {controller: FooCtrl, reloadOnSearch: false});
+
+ function FooCtrl() {
+ this.$watch(function() {
+ return $route.current.params;
+ }, function(scope, value) {
+ routeParams(value);
+ });
+ }
+
+ expect(routeParams).not.toHaveBeenCalled();
+
+ $location.updateHash('/foo');
+ scope.$digest();
+ expect(routeParams).toHaveBeenCalledWith({});
+ routeParams.reset();
+
+ // trigger reload
+ $location.hashSearch.foo = 'bar';
+ scope.$digest();
+ expect(routeParams).toHaveBeenCalledWith({foo: 'bar'});
+ routeParams.reset();
+
+ $location.updateHash('/bar/123');
+ scope.$digest();
+ expect(routeParams).toHaveBeenCalledWith({barId: '123'});
+ routeParams.reset();
+
+ // don't trigger reload
+ $location.hashSearch.foo = 'bar';
+ scope.$digest();
+ expect(routeParams).toHaveBeenCalledWith({barId: '123', foo: 'bar'});
+ });
+ });
});