From 4e27938389968f2e9987585e9d0b2e23204dae1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Tue, 26 Feb 2013 00:00:47 +0100 Subject: [PATCH] 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: 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: Closes #1050 Closes #1925 --- docs/content/guide/directive.ngdoc | 3 +++ src/ng/compile.js | 7 +++++- test/ng/compileSpec.js | 36 ++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/docs/content/guide/directive.ngdoc b/docs/content/guide/directive.ngdoc index da1dc6df9ab7..2eac3ede5726 100644 --- a/docs/content/guide/directive.ngdoc +++ b/docs/content/guide/directive.ngdoc @@ -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. diff --git a/src/ng/compile.js b/src/ng/compile.js index 48beb61ef51b..c56734c6d4e5 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -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') diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index b9ed3ff9d8e6..f8852b3cc832 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -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('')($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('')($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('')($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'); + })); + }); + });