From f9ff5af87c3f7bc51641e24f8b840a6044072096 Mon Sep 17 00:00:00 2001 From: David Rodenas Pico Date: Fri, 8 Apr 2016 11:40:28 +0200 Subject: [PATCH 1/2] perf(ngClass): optimize the case of static map of classes with large objects --- src/ng/directive/ngClass.js | 4 +++- test/ng/directive/ngClassSpec.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/ng/directive/ngClass.js b/src/ng/directive/ngClass.js index 2b258a4ac1ec..ec9d8dc8176f 100644 --- a/src/ng/directive/ngClass.js +++ b/src/ng/directive/ngClass.js @@ -1,6 +1,7 @@ 'use strict'; function classDirective(name, selector) { + var staticMapClassRegEx = /^\s*(::)?\s*\{/; name = 'ngClass' + name; return ['$animate', function($animate) { return { @@ -8,7 +9,8 @@ function classDirective(name, selector) { link: function(scope, element, attr) { var oldVal; - scope.$watch(attr[name], ngClassWatchAction, true); + // shortcut: if it is clearly a map of classes do not copy values, they are supposed to be boolean (truly/falsy) + scope[staticMapClassRegEx.test(attr[name]) ? '$watchCollection' : '$watch'](attr[name], ngClassWatchAction, true); attr.$observe('class', function(value) { ngClassWatchAction(scope.$eval(attr[name])); diff --git a/test/ng/directive/ngClassSpec.js b/test/ng/directive/ngClassSpec.js index a50f611e4b18..ccf1945e2057 100644 --- a/test/ng/directive/ngClassSpec.js +++ b/test/ng/directive/ngClassSpec.js @@ -409,6 +409,37 @@ describe('ngClass', function() { expect(e2.hasClass('even')).toBeTruthy(); expect(e2.hasClass('odd')).toBeFalsy(); })); + + describe('large objects', function() { + + var verylargeobject, getProp; + beforeEach(function() { + getProp = jasmine.createSpy('getProp'); + verylargeobject = {}; + Object.defineProperty(verylargeobject, 'prop', { + get: getProp, + enumerable: true + }); + }); + + it('should not copy large objects via hard map of classes', inject(function($rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.verylargeobject = verylargeobject; + $rootScope.$digest(); + + expect(getProp).not.toHaveBeenCalled(); + })); + + it('should not copy large objects via hard map of classes in one-time binding', inject(function($rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.verylargeobject = verylargeobject; + $rootScope.$digest(); + + expect(getProp).not.toHaveBeenCalled(); + })); + }); + + }); describe('ngClass animations', function() { From b938b4e4704a3042047879c952dd8c1328379f53 Mon Sep 17 00:00:00 2001 From: David Rodenas Pico Date: Sun, 10 Apr 2016 00:22:50 +0200 Subject: [PATCH 2/2] fix(ngClass): fix shallowCopy of mixed array and object --- src/ng/directive/ngClass.js | 6 +++++- test/ng/directive/ngClassSpec.js | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ng/directive/ngClass.js b/src/ng/directive/ngClass.js index ec9d8dc8176f..6e178705874f 100644 --- a/src/ng/directive/ngClass.js +++ b/src/ng/directive/ngClass.js @@ -80,7 +80,11 @@ function classDirective(name, selector) { updateClasses(oldClasses, newClasses); } } - oldVal = shallowCopy(newVal); + if (isArray(newVal)) { + oldVal = newVal.map(function(v) { return shallowCopy(v); }); + } else { + oldVal = shallowCopy(newVal); + } } } }; diff --git a/test/ng/directive/ngClassSpec.js b/test/ng/directive/ngClassSpec.js index ccf1945e2057..47dedb7ad9ff 100644 --- a/test/ng/directive/ngClassSpec.js +++ b/test/ng/directive/ngClassSpec.js @@ -410,6 +410,17 @@ describe('ngClass', function() { expect(e2.hasClass('odd')).toBeFalsy(); })); + it('should support mixed array/object variable with a mutating object', inject(function($rootScope, $compile) { + $rootScope.classVar = ['', {orange: true}]; + element = $compile('
')($rootScope); + $rootScope.$digest(); + + $rootScope.classVar[1].orange = false; + $rootScope.$digest(); + + expect(element.hasClass('orange')).toBeFalsy(); + })); + describe('large objects', function() { var verylargeobject, getProp; @@ -439,7 +450,6 @@ describe('ngClass', function() { })); }); - }); describe('ngClass animations', function() {