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

Commit dc0b0c7

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 6114c8f commit dc0b0c7

File tree

2 files changed

+165
-6
lines changed

2 files changed

+165
-6
lines changed

src/service/route.js

+30-6
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ angularServiceInject('$route', function($location) {
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) {
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;
148+
* }, function(scope, 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) {
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,9 +223,18 @@ angularServiceInject('$route', function($location) {
209223
function updateRoute(){
210224
var selectedRoute, pathParams, segmentMatch, key, redir;
211225

212-
if ($route.current && $route.current.scope) {
213-
$route.current.scope.$destroy();
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+
if ($route.current.scope) {
233+
$route.current.scope.$destroy();
234+
}
214235
}
236+
237+
lastHashPath = $location.hashPath;
215238
$route.current = null;
216239
// Match a route
217240
forEach(routes, function(rParams, rPath) {
@@ -255,19 +278,20 @@ angularServiceInject('$route', function($location) {
255278

256279
$route.current = extend({}, selectedRoute);
257280
$route.current.params = extend({}, $location.hashSearch, pathParams);
281+
lastRouteParams = pathParams;
258282
}
259283

260284
//fire onChange callbacks
261285
forEach(onChange, parentScope.$eval, parentScope);
262286

263-
// Create the scope if we have mtched a route
287+
// Create the scope if we have matched a route
264288
if ($route.current) {
265289
$route.current.scope = parentScope.$new($route.current.controller);
266290
}
267291
}
268292

269293

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

272296
return $route;
273297
}, ['$location']);

test/service/routeSpec.js

+135
Original file line numberDiff line numberDiff line change
@@ -260,4 +260,139 @@ describe('$route', function() {
260260
}
261261
});
262262
});
263+
264+
265+
describe('reloadOnSearch', function() {
266+
it('should reload a route when reloadOnSearch is enabled and hashSearch changes', function() {
267+
var scope = angular.scope(),
268+
$location = scope.$service('$location'),
269+
$route = scope.$service('$route'),
270+
reloaded = jasmine.createSpy('route reload');
271+
272+
$route.when('/foo', {controller: FooCtrl});
273+
$route.onChange(reloaded);
274+
275+
function FooCtrl() {
276+
reloaded();
277+
}
278+
279+
$location.updateHash('/foo');
280+
scope.$digest();
281+
expect(reloaded).toHaveBeenCalled();
282+
reloaded.reset();
283+
284+
// trigger reload
285+
$location.hashSearch.foo = 'bar';
286+
scope.$digest();
287+
expect(reloaded).toHaveBeenCalled();
288+
});
289+
290+
291+
it('should not reload a route when reloadOnSearch is disabled and only hashSearch changes',
292+
function() {
293+
var scope = angular.scope(),
294+
$location = scope.$service('$location'),
295+
$route = scope.$service('$route'),
296+
reloaded = jasmine.createSpy('route reload');
297+
298+
$route.when('/foo', {controller: FooCtrl, reloadOnSearch: false});
299+
$route.onChange(reloaded);
300+
301+
function FooCtrl() {
302+
reloaded();
303+
}
304+
305+
expect(reloaded).not.toHaveBeenCalled();
306+
307+
$location.updateHash('/foo');
308+
scope.$digest();
309+
expect(reloaded).toHaveBeenCalled();
310+
reloaded.reset();
311+
312+
// don't trigger reload
313+
$location.hashSearch.foo = 'bar';
314+
scope.$digest();
315+
expect(reloaded).not.toHaveBeenCalled();
316+
});
317+
318+
319+
it('should reload reloadOnSearch route when url differs only in route path param', function() {
320+
var scope = angular.scope(),
321+
$location = scope.$service('$location'),
322+
$route = scope.$service('$route'),
323+
reloaded = jasmine.createSpy('routeReload'),
324+
onRouteChange = jasmine.createSpy('onRouteChange');
325+
326+
$route.when('/foo/:fooId', {controller: FooCtrl, reloadOnSearch: false});
327+
$route.onChange(onRouteChange);
328+
329+
function FooCtrl() {
330+
reloaded();
331+
}
332+
333+
expect(reloaded).not.toHaveBeenCalled();
334+
expect(onRouteChange).not.toHaveBeenCalled();
335+
336+
$location.updateHash('/foo/aaa');
337+
scope.$digest();
338+
expect(reloaded).toHaveBeenCalled();
339+
expect(onRouteChange).toHaveBeenCalled();
340+
reloaded.reset();
341+
onRouteChange.reset();
342+
343+
$location.updateHash('/foo/bbb');
344+
scope.$digest();
345+
expect(reloaded).toHaveBeenCalled();
346+
expect(onRouteChange).toHaveBeenCalled();
347+
reloaded.reset();
348+
onRouteChange.reset();
349+
350+
$location.hashSearch.foo = 'bar';
351+
scope.$digest();
352+
expect(reloaded).not.toHaveBeenCalled();
353+
expect(onRouteChange).not.toHaveBeenCalled();
354+
});
355+
356+
357+
it('should update route params when reloadOnSearch is disabled and hashSearch', function() {
358+
var scope = angular.scope(),
359+
$location = scope.$service('$location'),
360+
$route = scope.$service('$route'),
361+
routeParams = jasmine.createSpy('routeParams');
362+
363+
$route.when('/foo', {controller: FooCtrl});
364+
$route.when('/bar/:barId', {controller: FooCtrl, reloadOnSearch: false});
365+
366+
function FooCtrl() {
367+
this.$watch(function() {
368+
return $route.current.params;
369+
}, function(scope, value) {
370+
routeParams(value);
371+
});
372+
}
373+
374+
expect(routeParams).not.toHaveBeenCalled();
375+
376+
$location.updateHash('/foo');
377+
scope.$digest();
378+
expect(routeParams).toHaveBeenCalledWith({});
379+
routeParams.reset();
380+
381+
// trigger reload
382+
$location.hashSearch.foo = 'bar';
383+
scope.$digest();
384+
expect(routeParams).toHaveBeenCalledWith({foo: 'bar'});
385+
routeParams.reset();
386+
387+
$location.updateHash('/bar/123');
388+
scope.$digest();
389+
expect(routeParams).toHaveBeenCalledWith({barId: '123'});
390+
routeParams.reset();
391+
392+
// don't trigger reload
393+
$location.hashSearch.foo = 'bar';
394+
scope.$digest();
395+
expect(routeParams).toHaveBeenCalledWith({barId: '123', foo: 'bar'});
396+
});
397+
});
263398
});

0 commit comments

Comments
 (0)