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

Commit 8b0258d

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. Closes #7739
1 parent 928c7ec commit 8b0258d

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
@@ -1033,17 +1033,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
10331033
directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective);
10341034

10351035
// iterate over the attributes
1036-
for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes,
1036+
for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
10371037
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
10381038
var attrStartName = false;
10391039
var attrEndName = false;
10401040

10411041
attr = nAttrs[j];
10421042
if (!msie || msie >= 8 || attr.specified) {
10431043
name = attr.name;
1044+
value = trim(attr.value);
1045+
10441046
// support ngAttr attribute binding
10451047
ngAttrName = directiveNormalize(name);
1046-
if (NG_ATTR_BINDING.test(ngAttrName)) {
1048+
if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
10471049
name = snake_case(ngAttrName.substr(6), '-');
10481050
}
10491051

@@ -1056,9 +1058,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
10561058

10571059
nName = directiveNormalize(name.toLowerCase());
10581060
attrsMap[nName] = name;
1059-
attrs[nName] = value = trim(attr.value);
1060-
if (getBooleanAttrName(node, nName)) {
1061-
attrs[nName] = true; // presence means true
1061+
if (isNgAttr || !attrs.hasOwnProperty(nName)) {
1062+
attrs[nName] = value;
1063+
if (getBooleanAttrName(node, nName)) {
1064+
attrs[nName] = true; // presence means true
1065+
}
10621066
}
10631067
addAttrInterpolateDirective(node, directives, value, nName);
10641068
addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,

test/ng/compileSpec.js

+77
Original file line numberDiff line numberDiff line change
@@ -5187,6 +5187,83 @@ describe('$compile', function() {
51875187
expect(element.attr('test')).toBe('Misko');
51885188
}));
51895189

5190+
it('should bind after digest but not before when after overridden attribute', inject(function($compile, $rootScope) {
5191+
$rootScope.name = "Misko";
5192+
element = $compile('<span test="123" ng-attr-test="{{name}}"></span>')($rootScope);
5193+
expect(element.attr('test')).toBe('123');
5194+
$rootScope.$digest();
5195+
expect(element.attr('test')).toBe('Misko');
5196+
}));
5197+
5198+
it('should bind after digest but not before when before overridden attribute', inject(function($compile, $rootScope) {
5199+
$rootScope.name = "Misko";
5200+
element = $compile('<span ng-attr-test="{{name}}" test="123"></span>')($rootScope);
5201+
expect(element.attr('test')).toBe('123');
5202+
$rootScope.$digest();
5203+
expect(element.attr('test')).toBe('Misko');
5204+
}));
5205+
5206+
5207+
describe('in directive', function() {
5208+
beforeEach(module(function() {
5209+
directive('syncTest', function(log) {
5210+
return {
5211+
link: {
5212+
pre: function(s, e, attr) { log(attr.test); },
5213+
post: function(s, e, attr) { log(attr.test); }
5214+
}
5215+
};
5216+
});
5217+
directive('asyncTest', function(log) {
5218+
return {
5219+
templateUrl: 'async.html',
5220+
link: {
5221+
pre: function(s, e, attr) { log(attr.test); },
5222+
post: function(s, e, attr) { log(attr.test); }
5223+
}
5224+
};
5225+
});
5226+
}));
5227+
5228+
beforeEach(inject(function($templateCache) {
5229+
$templateCache.put('async.html', '<h1>Test</h1>');
5230+
}));
5231+
5232+
it('should provide post-digest value in synchronous directive link functions when after overridden attribute',
5233+
inject(function(log, $rootScope, $compile) {
5234+
$rootScope.test = "TEST";
5235+
element = $compile('<div sync-test test="123" ng-attr-test="{{test}}"></div>')($rootScope);
5236+
expect(element.attr('test')).toBe('123');
5237+
expect(log.toArray()).toEqual(['TEST', 'TEST']);
5238+
}));
5239+
5240+
it('should provide post-digest value in synchronous directive link functions when before overridden attribute',
5241+
inject(function(log, $rootScope, $compile) {
5242+
$rootScope.test = "TEST";
5243+
element = $compile('<div sync-test ng-attr-test="{{test}}" test="123"></div>')($rootScope);
5244+
expect(element.attr('test')).toBe('123');
5245+
expect(log.toArray()).toEqual(['TEST', 'TEST']);
5246+
}));
5247+
5248+
5249+
it('should provide post-digest value in asynchronous directive link functions when after overridden attribute',
5250+
inject(function(log, $rootScope, $compile) {
5251+
$rootScope.test = "TEST";
5252+
element = $compile('<div async-test test="123" ng-attr-test="{{test}}"></div>')($rootScope);
5253+
expect(element.attr('test')).toBe('123');
5254+
$rootScope.$digest();
5255+
expect(log.toArray()).toEqual(['TEST', 'TEST']);
5256+
}));
5257+
5258+
it('should provide post-digest value in asynchronous directive link functions when before overridden attribute',
5259+
inject(function(log, $rootScope, $compile) {
5260+
$rootScope.test = "TEST";
5261+
element = $compile('<div async-test ng-attr-test="{{test}}" test="123"></div>')($rootScope);
5262+
expect(element.attr('test')).toBe('123');
5263+
$rootScope.$digest();
5264+
expect(log.toArray()).toEqual(['TEST', 'TEST']);
5265+
}));
5266+
});
51905267

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

0 commit comments

Comments
 (0)