diff --git a/src/ng/directive/ngClass.js b/src/ng/directive/ngClass.js index 2b258a4ac1ec..6e178705874f 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])); @@ -78,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 a50f611e4b18..47dedb7ad9ff 100644 --- a/test/ng/directive/ngClassSpec.js +++ b/test/ng/directive/ngClassSpec.js @@ -409,6 +409,47 @@ describe('ngClass', function() { expect(e2.hasClass('even')).toBeTruthy(); 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; + 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() {