Skip to content

Commit dc6a65b

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 attributes like ng-href or ng-src. This commit is a more general solution that allows prefixing any attribute with "ngAttr", "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 ngAttrCx="{{cx}}" ng-attr-cy="{{cy}}" ng:Attr:r="{{r}}"></circle> </svg> Closes angular#1925
1 parent 2508b47 commit dc6a65b

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

docs/content/guide/directive.ngdoc

+2-1
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,8 @@ link() or compile() functions - is a way of accessing:
484484

485485
* *normalized attribute names:* Since a directive such as 'ngBind' can be expressed in many ways
486486
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized accessed to
487-
the attributes.
487+
the attributes. Also, if the attribute is prefixed with 'ngAttr', 'ng-attr-', 'ng:attr:' or
488+
'ng_attr_' the prefix will be removed once the attribute is evaluated, allowing late binding.
488489

489490
* *directive inter-communication:* All directives share the same instance of the attributes
490491
object which allows the directives to use the attributes object as inter directive

src/ng/compile.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ function $CompileProvider($provide) {
156156
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
157157
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
158158
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
159-
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto):/;
159+
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto):/,
160+
NG_ATTR_DIRECTIVE_REGEXP = /^((x-|data-)?ng[-,:,_]?attr[-,:,_]?)(.+)/i;
160161

161162

162163
/**
@@ -519,6 +520,10 @@ function $CompileProvider($provide) {
519520
attr = nAttrs[j];
520521
if (attr.specified) {
521522
name = attr.name;
523+
// support late binding attributes
524+
if(NG_ATTR_DIRECTIVE_REGEXP.test(name)) {
525+
name = NG_ATTR_DIRECTIVE_REGEXP.exec(name)[3];
526+
}
522527
nName = directiveNormalize(name.toLowerCase());
523528
attrsMap[nName] = name;
524529
attrs[nName] = value = trim((msie && name == 'href')

test/ng/compileSpec.js

+22
Original file line numberDiff line numberDiff line change
@@ -2498,4 +2498,26 @@ 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 ngAttrTest="{{name}}" ng:attr:test2="{{name}}"></span>')($rootScope);
2515+
expect(element.attr('test')).toBeUndefined();
2516+
expect(element.attr('test2')).toBeUndefined();
2517+
$rootScope.$digest();
2518+
expect(element.attr('test')).toBe('Misko');
2519+
expect(element.attr('test2')).toBe('Misko');
2520+
}));
2521+
});
2522+
25012523
});

0 commit comments

Comments
 (0)