Skip to content
Permalink
Browse files

feat($compile): add support for ng-attr with camelCased attributes

SVG attributes are case sensitive and some have upper case letters in them
This change ensures that we can identify these, when being used with the `ng-attr`
directive, by encoding upper case letters with a preceding underscore.

For example to apply `ng-attr` to the `viewBox` attribute we could write
`ng-attr-view_box` - or any of the other variants: `ng:attr:view_box`,
`data-ng-attr-view_box`, etc.

Closes #9845
Closes #10194
  • Loading branch information
Wesley Cho authored and petebacondarwin committed Nov 23, 2014
1 parent ca4df47 commit d8e37078600089839f82f0e84022f1087e1fd3f2
Showing with 49 additions and 3 deletions.
  1. +9 −0 docs/content/guide/directive.ngdoc
  2. +4 −1 src/ng/compile.js
  3. +36 −2 test/ng/compileSpec.js
@@ -174,6 +174,15 @@ For example, we could fix the example above by instead writing:
</svg>
```

If one wants to modify a camelcased attribute (SVG elements have valid camelcased attributes), such as `viewBox` on the `svg` element, one can use underscores to denote that the attribute to bind to is naturally camelcased.

For example, to bind to `viewBox`, we can write:

```html
<svg ng-attr-view_box="{{viewBox}}">
</svg>
```


## Creating Directives

@@ -1420,7 +1420,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// support ngAttr attribute binding
ngAttrName = directiveNormalize(name);
if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
name = snake_case(ngAttrName.substr(6), '-');
name = name.replace(PREFIX_REGEXP, '')
.substr(8).replace(/_(.)/g, function(match, letter) {
return letter.toUpperCase();
});
}

var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
@@ -6542,16 +6542,21 @@ describe('$compile', function() {
expect(element.attr('href')).toBe('test/test');
}));

it('should work if they are prefixed with x- or data-', inject(function($compile, $rootScope) {
it('should work if they are prefixed with x- or data- and different prefixes', 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);
element = $compile('<span data-ng-attr-test2="{{name}}" x-ng-attr-test3="{{name}}" data-ng:attr-test4="{{name}}" ' +
'x_ng-attr-test5="{{name}}" data:ng-attr-test6="{{name}}"></span>')($rootScope);
expect(element.attr('test2')).toBeUndefined();
expect(element.attr('test3')).toBeUndefined();
expect(element.attr('test4')).toBeUndefined();
expect(element.attr('test5')).toBeUndefined();
expect(element.attr('test6')).toBeUndefined();
$rootScope.$digest();
expect(element.attr('test2')).toBe('Misko');
expect(element.attr('test3')).toBe('Misko');
expect(element.attr('test4')).toBe('Misko');
expect(element.attr('test5')).toBe('Misko');
expect(element.attr('test6')).toBe('Misko');
}));

describe('when an attribute has a dash-separated name', function() {
@@ -6619,6 +6624,35 @@ describe('$compile', function() {
});


describe('when an attribute has an underscore-separated name', function() {

it('should work with different prefixes', inject(function($compile, $rootScope) {
$rootScope.dimensions = "0 0 0 0";
element = $compile('<svg ng:attr:view_box="{{dimensions}}"></svg>')($rootScope);
expect(element.attr('viewBox')).toBeUndefined();
$rootScope.$digest();
expect(element.attr('viewBox')).toBe('0 0 0 0');
}));

it('should work if they are prefixed with x- or data-', inject(function($compile, $rootScope) {
$rootScope.dimensions = "0 0 0 0";
$rootScope.number = 0.42;
$rootScope.scale = 1;
element = $compile('<svg data-ng-attr-view_box="{{dimensions}}">' +
'<filter x-ng-attr-filter_units="{{number}}">' +
'<feDiffuseLighting data-ng:attr_surface_scale="{{scale}}">' +
'</feDiffuseLighting>' +
'<feSpecularLighting x-ng:attr_surface_scale="{{scale}}">' +
'</feSpecularLighting></filter></svg>')($rootScope);
expect(element.attr('viewBox')).toBeUndefined();
$rootScope.$digest();
expect(element.attr('viewBox')).toBe('0 0 0 0');
expect(element.find('filter').attr('filterUnits')).toBe('0.42');
expect(element.find('feDiffuseLighting').attr('surfaceScale')).toBe('1');
expect(element.find('feSpecularLighting').attr('surfaceScale')).toBe('1');
}));
});

describe('multi-element directive', function() {
it('should group on link function', inject(function($compile, $rootScope) {
$rootScope.show = false;

3 comments on commit d8e3707

@KingKarrow

This comment has been minimized.

Copy link

@KingKarrow KingKarrow replied May 27, 2016

Hi, this was in the 1.5.5 release right? That's the version I'm using (based on other dependencies), and I couldn't get this to work. Tried to set ng-attr-start_offset="{{offset}}" on an SVG element and it still came out all lowercase.

@petebacondarwin

This comment has been minimized.

Copy link
Member

@petebacondarwin petebacondarwin replied May 27, 2016

@KingKarrow it was actually released in 1.3.7 but it still appears in 1.5.5 and it works.
See http://plnkr.co/edit/7JiR9hxHju40omQmJLkS?p=preview

@KingKarrow

This comment has been minimized.

Copy link

@KingKarrow KingKarrow replied May 27, 2016

Okay, cool. At least I know it -should- work with our version. Thanks!

Please sign in to comment.
You can’t perform that action at this time.