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

Commit 09de7b5

Browse files
committed
feat($compile): use allOrNothing interpolation for ngAttr*
allOrNothing interpolation is now used for ng-attr-*, under all circumstances. This prevents uninitialized attributes from being added to the DOM with invalid values which cause errors to be shown. BREAKING CHANGE: Now, ng-attr-* will never add the attribute to the DOM if any of the interpolated expressions evaluate to `undefined`. To work around this, initialize values which are intended to be the empty string with the empty string: For example, given the following markup: <div ng-attr-style="border-radius: {{value}}{{units}}"></div> If $scope.value is `4`, and $scope.units is undefined, the resulting markup is unchanged: <div ng-attr-style="border-radius: {{value}}{{units}}"></div> However, if $scope.units is `""`, then the resulting markup is updated: <div ng-attr-style="border-radius: {{value}}{{units}}" style="border-radius: 4"></div> Closes #8376 Closes #8399
1 parent a7fb357 commit 09de7b5

File tree

3 files changed

+17
-4
lines changed

3 files changed

+17
-4
lines changed

docs/content/guide/directive.ngdoc

+3-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ With `ng-attr-cx` you can work around this problem.
161161
If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`)
162162
then during the binding will be applied to the corresponding unprefixed attribute. This allows
163163
you to bind to attributes that would otherwise be eagerly processed by browsers
164-
(e.g. an SVG element's `circle[cx]` attributes).
164+
(e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of
165+
{@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string
166+
results in `undefined`, the attribute is removed and not added to the element.
165167

166168
For example, we could fix the example above by instead writing:
167169

src/ng/compile.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1104,7 +1104,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
11041104
attrs[nName] = true; // presence means true
11051105
}
11061106
}
1107-
addAttrInterpolateDirective(node, directives, value, nName);
1107+
addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
11081108
addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
11091109
attrEndName);
11101110
}
@@ -1984,7 +1984,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
19841984
}
19851985

19861986

1987-
function addAttrInterpolateDirective(node, directives, value, name) {
1987+
function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
19881988
var interpolateFn = $interpolate(value, true);
19891989

19901990
// no interpolation found -> ignore
@@ -2013,7 +2013,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
20132013
// we need to interpolate again, in case the attribute value has been updated
20142014
// (e.g. by another directive's compile function)
20152015
interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name),
2016-
ALL_OR_NOTHING_ATTRS[name]);
2016+
ALL_OR_NOTHING_ATTRS[name] || allOrNothing);
20172017

20182018
// if attribute was updated so that there is no interpolation going on we don't want to
20192019
// register any observers

test/ng/compileSpec.js

+11
Original file line numberDiff line numberDiff line change
@@ -5579,6 +5579,17 @@ describe('$compile', function() {
55795579
expect(element.attr('test')).toBe('Misko');
55805580
}));
55815581

5582+
it('should remove attribute if any bindings are undefined', inject(function($compile, $rootScope) {
5583+
element = $compile('<span ng-attr-test="{{name}}{{emphasis}}"></span>')($rootScope);
5584+
$rootScope.$digest();
5585+
expect(element.attr('test')).toBeUndefined();
5586+
$rootScope.name = 'caitp';
5587+
$rootScope.$digest();
5588+
expect(element.attr('test')).toBeUndefined();
5589+
$rootScope.emphasis = '!!!';
5590+
$rootScope.$digest();
5591+
expect(element.attr('test')).toBe('caitp!!!');
5592+
}));
55825593

55835594
describe('in directive', function() {
55845595
beforeEach(module(function() {

0 commit comments

Comments
 (0)