diff --git a/src/ng/compile.js b/src/ng/compile.js index 56d36003f70b..aaef78ea1ca9 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -1010,7 +1010,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective); // iterate over the attributes - for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes, + for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { var attrStartName = false; var attrEndName = false; @@ -1018,9 +1018,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { attr = nAttrs[j]; if (!msie || msie >= 8 || attr.specified) { name = attr.name; + value = trim(attr.value); + // support ngAttr attribute binding ngAttrName = directiveNormalize(name); - if (NG_ATTR_BINDING.test(ngAttrName)) { + if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { name = snake_case(ngAttrName.substr(6), '-'); } @@ -1033,9 +1035,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { nName = directiveNormalize(name.toLowerCase()); attrsMap[nName] = name; - attrs[nName] = value = trim(attr.value); - if (getBooleanAttrName(node, nName)) { - attrs[nName] = true; // presence means true + if (isNgAttr || !attrs.hasOwnProperty(nName)) { + attrs[nName] = value; + if (getBooleanAttrName(node, nName)) { + attrs[nName] = true; // presence means true + } } addAttrInterpolateDirective(node, directives, value, nName); addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 480bde531a1d..6a044d1a953c 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -4901,6 +4901,83 @@ describe('$compile', function() { expect(element.attr('test')).toBe('Misko'); })); + it('should bind after digest but not before when after overridden attribute', inject(function($compile, $rootScope) { + $rootScope.name = "Misko"; + element = $compile('')($rootScope); + expect(element.attr('test')).toBe('123'); + $rootScope.$digest(); + expect(element.attr('test')).toBe('Misko'); + })); + + it('should bind after digest but not before when before overridden attribute', inject(function($compile, $rootScope) { + $rootScope.name = "Misko"; + element = $compile('')($rootScope); + expect(element.attr('test')).toBe('123'); + $rootScope.$digest(); + expect(element.attr('test')).toBe('Misko'); + })); + + + describe('in directive', function() { + beforeEach(module(function() { + directive('syncTest', function(log) { + return { + link: { + pre: function(s, e, attr) { log(attr.test); }, + post: function(s, e, attr) { log(attr.test); } + } + }; + }); + directive('asyncTest', function(log) { + return { + templateUrl: 'async.html', + link: { + pre: function(s, e, attr) { log(attr.test); }, + post: function(s, e, attr) { log(attr.test); } + } + }; + }); + })); + + beforeEach(inject(function($templateCache) { + $templateCache.put('async.html', '

Test

'); + })); + + it('should provide post-digest value in synchronous directive link functions when after overridden attribute', + inject(function(log, $rootScope, $compile) { + $rootScope.test = "TEST"; + element = $compile('
')($rootScope); + expect(element.attr('test')).toBe('123'); + expect(log.toArray()).toEqual(['TEST', 'TEST']); + })); + + it('should provide post-digest value in synchronous directive link functions when before overridden attribute', + inject(function(log, $rootScope, $compile) { + $rootScope.test = "TEST"; + element = $compile('
')($rootScope); + expect(element.attr('test')).toBe('123'); + expect(log.toArray()).toEqual(['TEST', 'TEST']); + })); + + + it('should provide post-digest value in asynchronous directive link functions when after overridden attribute', + inject(function(log, $rootScope, $compile) { + $rootScope.test = "TEST"; + element = $compile('
')($rootScope); + expect(element.attr('test')).toBe('123'); + $rootScope.$digest(); + expect(log.toArray()).toEqual(['TEST', 'TEST']); + })); + + it('should provide post-digest value in asynchronous directive link functions when before overridden attribute', + inject(function(log, $rootScope, $compile) { + $rootScope.test = "TEST"; + element = $compile('
')($rootScope); + expect(element.attr('test')).toBe('123'); + $rootScope.$digest(); + expect(log.toArray()).toEqual(['TEST', 'TEST']); + })); + }); it('should work with different prefixes', inject(function($compile, $rootScope) { $rootScope.name = "Misko";