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

Commit ed59370

Browse files
jbedardcaitp
authored andcommitted
fix($compile): bind ng-attr-* even if unbound attribute follows ng-attr-*
Previously, <element ng-attr-foo="{{binding}}" foo="bar"></element>'s "foo" attribute would always equal "bar", because the bound version was overwritten. This CL corrects this behaviour and ensures that the ordering of attributes does not have an effect on whether or not ng-attr-bound attributes do their work.
1 parent d8e5acf commit ed59370

File tree

2 files changed

+86
-5
lines changed

2 files changed

+86
-5
lines changed

src/ng/compile.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -1010,17 +1010,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
10101010
directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective);
10111011

10121012
// iterate over the attributes
1013-
for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes,
1013+
for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
10141014
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
10151015
var attrStartName = false;
10161016
var attrEndName = false;
10171017

10181018
attr = nAttrs[j];
10191019
if (!msie || msie >= 8 || attr.specified) {
10201020
name = attr.name;
1021+
value = trim(attr.value);
1022+
10211023
// support ngAttr attribute binding
10221024
ngAttrName = directiveNormalize(name);
1023-
if (NG_ATTR_BINDING.test(ngAttrName)) {
1025+
if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
10241026
name = snake_case(ngAttrName.substr(6), '-');
10251027
}
10261028

@@ -1033,9 +1035,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
10331035

10341036
nName = directiveNormalize(name.toLowerCase());
10351037
attrsMap[nName] = name;
1036-
attrs[nName] = value = trim(attr.value);
1037-
if (getBooleanAttrName(node, nName)) {
1038-
attrs[nName] = true; // presence means true
1038+
if (isNgAttr || !attrs.hasOwnProperty(nName)) {
1039+
attrs[nName] = value;
1040+
if (getBooleanAttrName(node, nName)) {
1041+
attrs[nName] = true; // presence means true
1042+
}
10391043
}
10401044
addAttrInterpolateDirective(node, directives, value, nName);
10411045
addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,

test/ng/compileSpec.js

+77
Original file line numberDiff line numberDiff line change
@@ -4901,6 +4901,83 @@ describe('$compile', function() {
49014901
expect(element.attr('test')).toBe('Misko');
49024902
}));
49034903

4904+
it('should bind after digest but not before when after overridden attribute', inject(function($compile, $rootScope) {
4905+
$rootScope.name = "Misko";
4906+
element = $compile('<span test="123" ng-attr-test="{{name}}"></span>')($rootScope);
4907+
expect(element.attr('test')).toBe('123');
4908+
$rootScope.$digest();
4909+
expect(element.attr('test')).toBe('Misko');
4910+
}));
4911+
4912+
it('should bind after digest but not before when before overridden attribute', inject(function($compile, $rootScope) {
4913+
$rootScope.name = "Misko";
4914+
element = $compile('<span ng-attr-test="{{name}}" test="123"></span>')($rootScope);
4915+
expect(element.attr('test')).toBe('123');
4916+
$rootScope.$digest();
4917+
expect(element.attr('test')).toBe('Misko');
4918+
}));
4919+
4920+
4921+
describe('in directive', function() {
4922+
beforeEach(module(function() {
4923+
directive('syncTest', function(log) {
4924+
return {
4925+
link: {
4926+
pre: function(s, e, attr) { log(attr.test); },
4927+
post: function(s, e, attr) { log(attr.test); }
4928+
}
4929+
};
4930+
});
4931+
directive('asyncTest', function(log) {
4932+
return {
4933+
templateUrl: 'async.html',
4934+
link: {
4935+
pre: function(s, e, attr) { log(attr.test); },
4936+
post: function(s, e, attr) { log(attr.test); }
4937+
}
4938+
};
4939+
});
4940+
}));
4941+
4942+
beforeEach(inject(function($templateCache) {
4943+
$templateCache.put('async.html', '<h1>Test</h1>');
4944+
}));
4945+
4946+
it('should provide post-digest value in synchronous directive link functions when after overridden attribute',
4947+
inject(function(log, $rootScope, $compile) {
4948+
$rootScope.test = "TEST";
4949+
element = $compile('<div sync-test test="123" ng-attr-test="{{test}}"></div>')($rootScope);
4950+
expect(element.attr('test')).toBe('123');
4951+
expect(log.toArray()).toEqual(['TEST', 'TEST']);
4952+
}));
4953+
4954+
it('should provide post-digest value in synchronous directive link functions when before overridden attribute',
4955+
inject(function(log, $rootScope, $compile) {
4956+
$rootScope.test = "TEST";
4957+
element = $compile('<div sync-test ng-attr-test="{{test}}" test="123"></div>')($rootScope);
4958+
expect(element.attr('test')).toBe('123');
4959+
expect(log.toArray()).toEqual(['TEST', 'TEST']);
4960+
}));
4961+
4962+
4963+
it('should provide post-digest value in asynchronous directive link functions when after overridden attribute',
4964+
inject(function(log, $rootScope, $compile) {
4965+
$rootScope.test = "TEST";
4966+
element = $compile('<div async-test test="123" ng-attr-test="{{test}}"></div>')($rootScope);
4967+
expect(element.attr('test')).toBe('123');
4968+
$rootScope.$digest();
4969+
expect(log.toArray()).toEqual(['TEST', 'TEST']);
4970+
}));
4971+
4972+
it('should provide post-digest value in asynchronous directive link functions when before overridden attribute',
4973+
inject(function(log, $rootScope, $compile) {
4974+
$rootScope.test = "TEST";
4975+
element = $compile('<div async-test ng-attr-test="{{test}}" test="123"></div>')($rootScope);
4976+
expect(element.attr('test')).toBe('123');
4977+
$rootScope.$digest();
4978+
expect(log.toArray()).toEqual(['TEST', 'TEST']);
4979+
}));
4980+
});
49044981

49054982
it('should work with different prefixes', inject(function($compile, $rootScope) {
49064983
$rootScope.name = "Misko";

0 commit comments

Comments
 (0)