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

Commit 98247bc

Browse files
gkalpakThomasBurleson
authored andcommitted
fix(mdMedia): avoid unnecessary digest and make changes apply quicker
Previously, the `updateAll()` method relied on `$timeout` for scheduling a digest. This made the possible changes apply after the next rendering and could possibly lead to extra digests. Replaces the call to `$timeout` with a call to `$evalAsync()`, which avoids an extra digest if one is already in progress and also applies the changes before the next rendering. Closes #978.
1 parent 8736c7c commit 98247bc

3 files changed

Lines changed: 78 additions & 18 deletions

File tree

src/core/util/media.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ angular.module('material.core')
1010
* @example $mdMedia('(min-width: 1200px)') == true if device-width >= 1200px
1111
* @example $mdMedia('max-width: 300px') == true if device-width <= 300px (sanitizes input, adding parens)
1212
*/
13-
function mdMediaFactory($window, $mdUtil, $timeout, $mdConstant) {
13+
function mdMediaFactory($mdConstant, $mdUtil, $rootScope, $window) {
1414
var queriesCache = $mdUtil.cacheFactory('$mdMedia:queries', {capacity: 15});
1515
var resultsCache = $mdUtil.cacheFactory('$mdMedia:results', {capacity: 15});
1616

@@ -42,13 +42,16 @@ function mdMediaFactory($window, $mdUtil, $timeout, $mdConstant) {
4242
}
4343

4444
function updateAll() {
45-
var keys = cache.keys();
46-
if (keys.length) {
47-
for (var i = 0, ii = keys.length; i < ii; i++) {
48-
cache.put(keys[i], !!$window.matchMedia(keys[i]).matches);
45+
var keys = resultsCache.keys();
46+
var len = keys.length;
47+
48+
if (len) {
49+
for (var i = 0; i < len; i++) {
50+
add(keys[i]);
4951
}
50-
// trigger a $digest()
51-
$timeout(angular.noop);
52+
53+
// Trigger a $digest() if not already in progress
54+
$rootScope.$evalAsync();
5255
}
5356
}
5457
}

src/core/util/media.spec.js

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,81 @@
11
describe('$mdMedia', function() {
2+
var matchMediaResult;
3+
var queriesCache;
4+
var resultsCache;
5+
26

37
beforeEach(module('material.core'));
48

5-
var matchMediaResult = false;
6-
beforeEach(inject(function($window) {
9+
beforeEach(inject(function($cacheFactory, $mdMedia, $window) {
10+
matchMediaResult = false;
11+
12+
queriesCache = $cacheFactory.get('$mdMedia:queries');
13+
resultsCache = $cacheFactory.get('$mdMedia:results');
14+
715
spyOn($window, 'matchMedia').andCallFake(function() {
8-
return { matches: matchMediaResult };
16+
return {matches: matchMediaResult};
917
});
1018
}));
1119

12-
it('should validate input', inject(function($window, $mdMedia) {
20+
afterEach(function() {
21+
queriesCache.removeAll();
22+
resultsCache.removeAll();
23+
});
24+
25+
26+
it('should look up queries in `$mdConstant.MEDIA`', inject(
27+
function($mdConstant, $mdMedia, $window) {
28+
$mdConstant.MEDIA.somePreset = 'someQuery';
29+
30+
$mdMedia('somePreset');
31+
expect($window.matchMedia).toHaveBeenCalledWith('someQuery');
32+
33+
delete($mdConstant.MEDIA.somePreset);
34+
}
35+
));
36+
37+
it('should look up validated queries in `queriesCache`', inject(function($mdMedia, $window) {
38+
queriesCache.put('originalQuery', 'validatedQuery');
39+
40+
$mdMedia('originalQuery');
41+
expect($window.matchMedia).toHaveBeenCalledWith('validatedQuery');
42+
}));
43+
44+
it('should validate queries', inject(function($mdMedia, $window) {
1345
$mdMedia('something');
1446
expect($window.matchMedia).toHaveBeenCalledWith('(something)');
1547
}));
1648

17-
it('should return result of matchMedia and recalculate on resize', inject(function($window, $mdMedia) {
49+
it('should cache validated queries in `queriesCache`', inject(function($mdMedia) {
50+
$mdMedia('query');
51+
expect(queriesCache.get('query')).toBe('(query)');
52+
}));
53+
54+
it('should return cached results if available', inject(function($mdMedia) {
55+
resultsCache.put('(query)', 'result');
56+
expect($mdMedia('(query)')).toBe('result');
57+
}));
58+
59+
it('should cache results in `resultsCache`', inject(function($mdMedia) {
60+
$mdMedia('(query)');
61+
expect(resultsCache.get('(query)')).toBe(false);
62+
}));
63+
64+
it('should recalculate on resize', inject(function($mdMedia, $window) {
1865
matchMediaResult = true;
19-
expect($mdMedia('foo')).toBe(true);
66+
expect($mdMedia('query')).toBe(true);
67+
expect($window.matchMedia.callCount).toBe(1);
68+
69+
expect($mdMedia('query')).toBe(true);
70+
expect($window.matchMedia.callCount).toBe(1);
71+
2072
matchMediaResult = false;
21-
expect($mdMedia('foo')).toBe(true);
73+
expect($mdMedia('query')).toBe(true);
74+
expect($window.matchMedia.callCount).toBe(1);
75+
2276
angular.element($window).triggerHandler('resize');
23-
expect($mdMedia('foo')).toBe(false);
77+
78+
expect($mdMedia('query')).toBe(false);
79+
expect($window.matchMedia.callCount).toBe(2);
2480
}));
2581
});

src/core/util/util.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,9 @@ angular.module('material.core')
261261
}
262262

263263
/*
264-
* Angular's $cacheFactory doesn't have a keys() method,
265-
* so we add one ourself.
264+
* Inject a 'keys()' method into Angular's $cacheFactory. Then
265+
* head-hook all other methods
266+
*
266267
*/
267268
function cacheFactory(id, options) {
268269
var cache = $cacheFactory(id, options);
@@ -288,7 +289,7 @@ angular.module('material.core')
288289

289290
cache._destroy = cache.destroy;
290291
cache.destroy = function() {
291-
keys = null;
292+
keys = {};
292293
return cache._destroy();
293294
};
294295

0 commit comments

Comments
 (0)