Skip to content

Commit

Permalink
feat($compile): Allow late binding attributes
Browse files Browse the repository at this point in the history
Sometimes is not desirable to use interpolation on attributes because
the user agent parses them before the interpolation takes place. I.e:

<svg>
  <circle cx="{{cx}}" cy="{{cy}}" r="{{r}}"></circle>
</svg>

The snippet throws three browser errors, one for each attribute.

For some attributes, AngularJS fixes that behaviour introducing special
directives like ng-href or ng-src.

This commit is a more general solution that allows prefixing any
attribute with "ng-attr-", "ng:attr:" or "ng_attr_"  so it will
be set only when the binding is done. The prefix is then removed.

I.e:
<svg>
  <circle ng_attr_cx="{{cx}}" ng-attr-cy="{{cy}}" ng:Attr:r="{{r}}"></circle>
</svg>

Closes angular#1050
Closes angular#1925
  • Loading branch information
lrlopez authored and IgorMinar committed Feb 27, 2013
1 parent 86d191e commit 4e27938
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
3 changes: 3 additions & 0 deletions docs/content/guide/directive.ngdoc
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,9 @@ link() or compile() functions - is a way of accessing:
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized accessed to
the attributes.

* *late binding attributes: If the attribute is prefixed with 'ng-attr-', 'ng:attr:' or 'ng_attr_'
the prefix will be removed once the attribute is evaluated, allowing late binding.

* *directive inter-communication:* All directives share the same instance of the attributes
object which allows the directives to use the attributes object as inter directive
communication.
Expand Down
7 changes: 6 additions & 1 deletion src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,11 +514,16 @@ function $CompileProvider($provide) {
directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority);

// iterate over the attributes
for (var attr, name, nName, value, nAttrs = node.attributes,
for (var attr, name, nName, lName, value, nAttrs = node.attributes,
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
attr = nAttrs[j];
if (attr.specified) {
name = attr.name;
// support late binding attributes
lName = directiveNormalize(name);
if (/ngAttr[A-Z]/.test(lName)) {
name = lName.substr(6).toLowerCase();
}
nName = directiveNormalize(name.toLowerCase());
attrsMap[nName] = name;
attrs[nName] = value = trim((msie && name == 'href')
Expand Down
36 changes: 36 additions & 0 deletions test/ng/compileSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2591,4 +2591,40 @@ describe('$compile', function() {
});
});
});

describe('late binding attributes', function() {

it('should bind after digest but not before', inject(function($compile, $rootScope) {
$rootScope.name = "Misko";
element = $compile('<span ng-attr-test="{{name}}"></span>')($rootScope);
expect(element.attr('test')).toBeUndefined();
$rootScope.$digest();
expect(element.attr('test')).toBe('Misko');
}));

it('should work with different prefixes', inject(function($compile, $rootScope) {
$rootScope.name = "Misko";
element = $compile('<span ng:attr:test="{{name}}" ng-Attr-test2="{{name}}" ng_Attr_test3="{{name}}"></span>')($rootScope);
expect(element.attr('test')).toBeUndefined();
expect(element.attr('test2')).toBeUndefined();
expect(element.attr('test2')).toBeUndefined();
$rootScope.$digest();
expect(element.attr('test')).toBe('Misko');
expect(element.attr('test2')).toBe('Misko');
expect(element.attr('test3')).toBe('Misko');
}));

it('should work if they are prefixed with x- or data-', inject(function($compile, $rootScope) {
$rootScope.name = "Misko";
element = $compile('<span data-ng-attr-test2="{{name}}" x-ng-attr-test3="{{name}}" data-ng:attr-test4="{{name}}"></span>')($rootScope);
expect(element.attr('test2')).toBeUndefined();
expect(element.attr('test3')).toBeUndefined();
expect(element.attr('test4')).toBeUndefined();
$rootScope.$digest();
expect(element.attr('test2')).toBe('Misko');
expect(element.attr('test3')).toBe('Misko');
expect(element.attr('test4')).toBe('Misko');
}));
});

});

0 comments on commit 4e27938

Please sign in to comment.