Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
feat($route): add reloadOnSearch route param to avoid reloads
Browse files Browse the repository at this point in the history
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
  • Loading branch information
IgorMinar committed Aug 19, 2011
1 parent 4ec1d8e commit e004378
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 3 deletions.
29 changes: 26 additions & 3 deletions src/service/route.js
Expand Up @@ -68,6 +68,8 @@ angularServiceInject('$route', function(location, $updateView) {
matcher = switchRouteMatcher,
parentScope = this,
dirty = 0,
lastHashPath,
lastRouteParams,
$route = {
routes: routes,

Expand Down Expand Up @@ -136,6 +138,18 @@ angularServiceInject('$route', function(location, $updateView) {
* 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.myHashSearchParam;
* }, function(params) {
* //do stuff with params
* });
* }
*
* @returns {Object} route object
*
* @description
Expand All @@ -144,8 +158,8 @@ angularServiceInject('$route', function(location, $updateView) {
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;
},
Expand Down Expand Up @@ -209,6 +223,14 @@ angularServiceInject('$route', function(location, $updateView) {
function updateRoute(){
var childScope, routeParams, pathParams, segmentMatch, key, redir;

if ($route.current) {
if (!$route.current.reloadOnSearch && (lastHashPath == location.hashPath)) {
$route.current.params = extend({}, location.hashSearch, lastRouteParams);
return;
}
}

lastHashPath = location.hashPath;
$route.current = null;
forEach(routes, function(rParams, rPath) {
if (!pathParams) {
Expand Down Expand Up @@ -255,6 +277,7 @@ angularServiceInject('$route', function(location, $updateView) {
scope: childScope,
params: extend({}, location.hashSearch, pathParams)
});
lastRouteParams = pathParams;
}

//fire onChange callbacks
Expand All @@ -266,7 +289,7 @@ angularServiceInject('$route', function(location, $updateView) {
}


this.$watch(function(){return dirty + location.hash;}, updateRoute);
this.$watch(function(){ return dirty + location.hash; }, updateRoute);

return $route;
}, ['$location', '$updateView']);
136 changes: 136 additions & 0 deletions test/service/routeSpec.js
Expand Up @@ -227,4 +227,140 @@ 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.$eval();
expect(reloaded).toHaveBeenCalled();
reloaded.reset();

// trigger reload
$location.hashSearch.foo = 'bar';
scope.$eval();
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.$eval();
expect(reloaded).toHaveBeenCalled();
reloaded.reset();

// don't trigger reload
$location.hashSearch.foo = 'bar';
scope.$eval();
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.$eval();
expect(reloaded).toHaveBeenCalled();
expect(onRouteChange).toHaveBeenCalled();
reloaded.reset();
onRouteChange.reset();

$location.updateHash('/foo/bbb');
scope.$eval();
expect(reloaded).toHaveBeenCalled();
expect(onRouteChange).toHaveBeenCalled();
reloaded.reset();
onRouteChange.reset();

$location.hashSearch.foo = 'bar';
scope.$eval();
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(params) {
routeParams(params);
});
}

expect(routeParams).not.toHaveBeenCalled();

$location.updateHash('/foo');
scope.$eval();
expect(routeParams).toHaveBeenCalledWith({});
routeParams.reset();

// trigger reload
$location.hashSearch.foo = 'bar';
scope.$eval();
expect(routeParams).toHaveBeenCalledWith({foo: 'bar'});
routeParams.reset();

$location.updateHash('/bar/123');
scope.$eval();
expect(routeParams).toHaveBeenCalledWith({barId: '123'});
routeParams.reset();

// don't trigger reload
$location.hashSearch.foo = 'bar';
scope.$eval();
$route.current.scope.$eval(); // ng:view propagates evals so we have to do it by hand here
expect(routeParams).toHaveBeenCalledWith({barId: '123', foo: 'bar'});
});
});
});

0 comments on commit e004378

Please sign in to comment.