Skip to content

Commit 638e6fd

Browse files
committed
feat($compile): Allow late binding attributes
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
1 parent 2508b47 commit 638e6fd

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

docs/content/guide/directive.ngdoc

+3
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,9 @@ link() or compile() functions - is a way of accessing:
486486
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized accessed to
487487
the attributes.
488488

489+
* *late binding attributes: If the attribute is prefixed with 'ng-attr-', 'ng:attr:' or 'ng_attr_'
490+
the prefix will be removed once the attribute is evaluated, allowing late binding.
491+
489492
* *directive inter-communication:* All directives share the same instance of the attributes
490493
object which allows the directives to use the attributes object as inter directive
491494
communication.

src/ng/compile.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -514,11 +514,16 @@ function $CompileProvider($provide) {
514514
directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority);
515515

516516
// iterate over the attributes
517-
for (var attr, name, nName, value, nAttrs = node.attributes,
517+
for (var attr, name, nName, lName, value, nAttrs = node.attributes,
518518
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
519519
attr = nAttrs[j];
520520
if (attr.specified) {
521521
name = attr.name;
522+
// support late binding attributes
523+
lName = directiveNormalize(name);
524+
if (/ngAttr[A-Z]/.test(lName)) {
525+
name = lName.substr(6).toLowerCase();
526+
}
522527
nName = directiveNormalize(name.toLowerCase());
523528
attrsMap[nName] = name;
524529
attrs[nName] = value = trim((msie && name == 'href')

test/ng/compileSpec.js

+36
Original file line numberDiff line numberDiff line change
@@ -2498,4 +2498,40 @@ describe('$compile', function() {
24982498
});
24992499
});
25002500
});
2501+
2502+
describe('late binding attributes', function() {
2503+
2504+
it('should bind after digest but not before', inject(function($compile, $rootScope) {
2505+
$rootScope.name = "Misko";
2506+
element = $compile('<span ng-attr-test="{{name}}"></span>')($rootScope);
2507+
expect(element.attr('test')).toBeUndefined();
2508+
$rootScope.$digest();
2509+
expect(element.attr('test')).toBe('Misko');
2510+
}));
2511+
2512+
it('should work with different prefixes', inject(function($compile, $rootScope) {
2513+
$rootScope.name = "Misko";
2514+
element = $compile('<span ng:attr:test="{{name}}" ng-Attr-test2="{{name}}" ng_Attr_test3="{{name}}"></span>')($rootScope);
2515+
expect(element.attr('test')).toBeUndefined();
2516+
expect(element.attr('test2')).toBeUndefined();
2517+
expect(element.attr('test2')).toBeUndefined();
2518+
$rootScope.$digest();
2519+
expect(element.attr('test')).toBe('Misko');
2520+
expect(element.attr('test2')).toBe('Misko');
2521+
expect(element.attr('test3')).toBe('Misko');
2522+
}));
2523+
2524+
it('should work if they are prefixed with x- or data-', inject(function($compile, $rootScope) {
2525+
$rootScope.name = "Misko";
2526+
element = $compile('<span data-ng-attr-test2="{{name}}" x-ng-attr-test3="{{name}}" data-ng:attr-test4="{{name}}"></span>')($rootScope);
2527+
expect(element.attr('test2')).toBeUndefined();
2528+
expect(element.attr('test3')).toBeUndefined();
2529+
expect(element.attr('test4')).toBeUndefined();
2530+
$rootScope.$digest();
2531+
expect(element.attr('test2')).toBe('Misko');
2532+
expect(element.attr('test3')).toBe('Misko');
2533+
expect(element.attr('test4')).toBe('Misko');
2534+
}));
2535+
});
2536+
25012537
});

0 commit comments

Comments
 (0)