mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
feat($compile): add attribute binding support via ngAttr*
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.
Example usage:
<svg>
<circle ng-attr-cx="{{cx}}" ng-attr-cy="{{cy}}" ng:attr-r="{{r}}"></circle>
</svg>
Closes #1050
Closes #1925
This commit is contained in:
parent
86d191ed4a
commit
cf17c6af47
3 changed files with 71 additions and 3 deletions
|
|
@ -53,7 +53,7 @@ the following example.
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
# String interpolation
|
||||
# Text and attribute bindings
|
||||
|
||||
During the compilation process the {@link api/ng.$compile compiler} matches text and
|
||||
attributes using the {@link api/ng.$interpolate $interpolate} service to see if they
|
||||
|
|
@ -66,6 +66,31 @@ here:
|
|||
<a href="img/{{username}}.jpg">Hello {{username}}!</a>
|
||||
</pre>
|
||||
|
||||
|
||||
# ngAttr attribute bindings
|
||||
|
||||
If an attribute with a binding is prefixed with `ngAttr` prefix (denormalized prefix: 'ng-attr-',
|
||||
'ng:attr-') then during the compilation the prefix will be removed and the binding will be applied
|
||||
to an unprefixed attribute. This allows binding to attributes that would otherwise be eagerly
|
||||
processed by browsers in their uncompilled form (e.g. `img[src]` or svg's `circle[cx]` attributes).
|
||||
|
||||
For example, considering template:
|
||||
|
||||
<svg>
|
||||
<circle ng-attr-cx="{{cx}}"></circle>
|
||||
</svg>
|
||||
|
||||
and model cx set to 5, will result in rendering this dom:
|
||||
|
||||
<svg>
|
||||
<circle cx="5"></circle>
|
||||
</svg>
|
||||
|
||||
If you were to bind `{{cx}}` directly to the `cx` attribute, you'd get the following error:
|
||||
`Error: Invalid value for attribute cx="{{cx}}"`. With `ng-attr-cx` you can work around this
|
||||
problem.
|
||||
|
||||
|
||||
# Compilation process, and directive matching
|
||||
|
||||
Compilation of HTML happens in three phases:
|
||||
|
|
|
|||
|
|
@ -349,7 +349,8 @@ function $CompileProvider($provide) {
|
|||
? identity
|
||||
: function denormalizeTemplate(template) {
|
||||
return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
|
||||
};
|
||||
},
|
||||
NG_ATTR_BINDING = /^ngAttr[A-Z]/;
|
||||
|
||||
|
||||
return compile;
|
||||
|
|
@ -514,11 +515,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, ngAttrName, value, nAttrs = node.attributes,
|
||||
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
|
||||
attr = nAttrs[j];
|
||||
if (attr.specified) {
|
||||
name = attr.name;
|
||||
// support ngAttr attribute binding
|
||||
ngAttrName = directiveNormalize(name);
|
||||
if (NG_ATTR_BINDING.test(ngAttrName)) {
|
||||
name = ngAttrName.substr(6).toLowerCase();
|
||||
}
|
||||
nName = directiveNormalize(name.toLowerCase());
|
||||
attrsMap[nName] = name;
|
||||
attrs[nName] = value = trim((msie && name == 'href')
|
||||
|
|
|
|||
|
|
@ -2591,4 +2591,41 @@ describe('$compile', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngAttr* attribute binding', 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');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue