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

Commit e004378

Browse files
committed
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
1 parent 4ec1d8e commit e004378

File tree

2 files changed

+162
-3
lines changed

2 files changed

+162
-3
lines changed

src/service/route.js

+26-3
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ angularServiceInject('$route', function(location, $updateView) {
6868
matcher = switchRouteMatcher,
6969
parentScope = this,
7070
dirty = 0,
71+
lastHashPath,
72+
lastRouteParams,
7173
$route = {
7274
routes: routes,
7375

@@ -136,6 +138,18 @@ angularServiceInject('$route', function(location, $updateView) {
136138
* The custom `redirectTo` function is expected to return a string which will be used
137139
* to update `$location.hash`.
138140
*
141+
* - `[reloadOnSearch=true]` - {boolean=} - reload route when $location.hashSearch
142+
* changes. If this option is disabled, you should set up a $watch to be notified of
143+
* param (hashSearch) changes as follows:
144+
*
145+
* function MyCtrl($route) {
146+
* this.$watch(function() {
147+
* return $route.current.params.myHashSearchParam;
148+
* }, function(params) {
149+
* //do stuff with params
150+
* });
151+
* }
152+
*
139153
* @returns {Object} route object
140154
*
141155
* @description
@@ -144,8 +158,8 @@ angularServiceInject('$route', function(location, $updateView) {
144158
when:function (path, params) {
145159
if (isUndefined(path)) return routes; //TODO(im): remove - not needed!
146160
var route = routes[path];
147-
if (!route) route = routes[path] = {};
148-
if (params) extend(route, params);
161+
if (!route) route = routes[path] = {reloadOnSearch: true};
162+
if (params) extend(route, params); //TODO(im): what the heck? merge two route definitions?
149163
dirty++;
150164
return route;
151165
},
@@ -209,6 +223,14 @@ angularServiceInject('$route', function(location, $updateView) {
209223
function updateRoute(){
210224
var childScope, routeParams, pathParams, segmentMatch, key, redir;
211225

226+
if ($route.current) {
227+
if (!$route.current.reloadOnSearch && (lastHashPath == location.hashPath)) {
228+
$route.current.params = extend({}, location.hashSearch, lastRouteParams);
229+
return;
230+
}
231+
}
232+
233+
lastHashPath = location.hashPath;
212234
$route.current = null;
213235
forEach(routes, function(rParams, rPath) {
214236
if (!pathParams) {
@@ -255,6 +277,7 @@ angularServiceInject('$route', function(location, $updateView) {
255277
scope: childScope,
256278
params: extend({}, location.hashSearch, pathParams)
257279
});
280+
lastRouteParams = pathParams;
258281
}
259282

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

268291

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

271294
return $route;
272295
}, ['$location', '$updateView']);

test/service/routeSpec.js

+136
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,140 @@ describe('$route', function() {
227227
}
228228
});
229229
});
230+
231+
232+
describe('reloadOnSearch', function() {
233+
it('should reload a route when reloadOnSearch is enabled and hashSearch changes', function() {
234+
var scope = angular.scope(),
235+
$location = scope.$service('$location'),
236+
$route = scope.$service('$route'),
237+
reloaded = jasmine.createSpy('route reload');
238+
239+
$route.when('/foo', {controller: FooCtrl});
240+
$route.onChange(reloaded);
241+
242+
function FooCtrl() {
243+
reloaded();
244+
}
245+
246+
$location.updateHash('/foo');
247+
scope.$eval();
248+
expect(reloaded).toHaveBeenCalled();
249+
reloaded.reset();
250+
251+
// trigger reload
252+
$location.hashSearch.foo = 'bar';
253+
scope.$eval();
254+
expect(reloaded).toHaveBeenCalled();
255+
});
256+
257+
258+
it('should not reload a route when reloadOnSearch is disabled and only hashSearch changes',
259+
function() {
260+
var scope = angular.scope(),
261+
$location = scope.$service('$location'),
262+
$route = scope.$service('$route'),
263+
reloaded = jasmine.createSpy('route reload');
264+
265+
$route.when('/foo', {controller: FooCtrl, reloadOnSearch: false});
266+
$route.onChange(reloaded);
267+
268+
function FooCtrl() {
269+
reloaded();
270+
}
271+
272+
expect(reloaded).not.toHaveBeenCalled();
273+
274+
$location.updateHash('/foo');
275+
scope.$eval();
276+
expect(reloaded).toHaveBeenCalled();
277+
reloaded.reset();
278+
279+
// don't trigger reload
280+
$location.hashSearch.foo = 'bar';
281+
scope.$eval();
282+
expect(reloaded).not.toHaveBeenCalled();
283+
});
284+
285+
286+
it('should reload reloadOnSearch route when url differs only in route path param', function() {
287+
var scope = angular.scope(),
288+
$location = scope.$service('$location'),
289+
$route = scope.$service('$route'),
290+
reloaded = jasmine.createSpy('routeReload'),
291+
onRouteChange = jasmine.createSpy('onRouteChange');
292+
293+
$route.when('/foo/:fooId', {controller: FooCtrl, reloadOnSearch: false});
294+
$route.onChange(onRouteChange);
295+
296+
function FooCtrl() {
297+
reloaded();
298+
}
299+
300+
expect(reloaded).not.toHaveBeenCalled();
301+
expect(onRouteChange).not.toHaveBeenCalled();
302+
303+
$location.updateHash('/foo/aaa');
304+
scope.$eval();
305+
expect(reloaded).toHaveBeenCalled();
306+
expect(onRouteChange).toHaveBeenCalled();
307+
reloaded.reset();
308+
onRouteChange.reset();
309+
310+
$location.updateHash('/foo/bbb');
311+
scope.$eval();
312+
expect(reloaded).toHaveBeenCalled();
313+
expect(onRouteChange).toHaveBeenCalled();
314+
reloaded.reset();
315+
onRouteChange.reset();
316+
317+
$location.hashSearch.foo = 'bar';
318+
scope.$eval();
319+
expect(reloaded).not.toHaveBeenCalled();
320+
expect(onRouteChange).not.toHaveBeenCalled();
321+
});
322+
323+
324+
it('should update route params when reloadOnSearch is disabled and hashSearch', function() {
325+
var scope = angular.scope(),
326+
$location = scope.$service('$location'),
327+
$route = scope.$service('$route'),
328+
routeParams = jasmine.createSpy('routeParams');
329+
330+
$route.when('/foo', {controller: FooCtrl});
331+
$route.when('/bar/:barId', {controller: FooCtrl, reloadOnSearch: false});
332+
333+
function FooCtrl() {
334+
this.$watch(function() {
335+
return $route.current.params;
336+
}, function(params) {
337+
routeParams(params);
338+
});
339+
}
340+
341+
expect(routeParams).not.toHaveBeenCalled();
342+
343+
$location.updateHash('/foo');
344+
scope.$eval();
345+
expect(routeParams).toHaveBeenCalledWith({});
346+
routeParams.reset();
347+
348+
// trigger reload
349+
$location.hashSearch.foo = 'bar';
350+
scope.$eval();
351+
expect(routeParams).toHaveBeenCalledWith({foo: 'bar'});
352+
routeParams.reset();
353+
354+
$location.updateHash('/bar/123');
355+
scope.$eval();
356+
expect(routeParams).toHaveBeenCalledWith({barId: '123'});
357+
routeParams.reset();
358+
359+
// don't trigger reload
360+
$location.hashSearch.foo = 'bar';
361+
scope.$eval();
362+
$route.current.scope.$eval(); // ng:view propagates evals so we have to do it by hand here
363+
expect(routeParams).toHaveBeenCalledWith({barId: '123', foo: 'bar'});
364+
});
365+
});
230366
});

0 commit comments

Comments
 (0)