Angular.js: "invalid value for attribute" errors at startup for svg elements

Created on 13 Jun 2012  ·  14Comments  ·  Source: angular/angular.js

Using chrome Version "20.0.1132.27 beta" (but also seen on other versions)

For this html (obviously it's simplified from the original):

<html ng-app>
  <head>
    <script src="http://code.angularjs.org/angular-1.0.0rc12.min.js"></script>
  </head>
  <body>
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <rect x="{{0}}" y="{{0}}" width="{{100}}" height="{{100}}" fill="red"/>
    </svg>
  </body>
</html>

it draws the expected rectangle, but when I look in the javascript console I see:

Error: Invalid value for attribute x="{{0}}" bug.html:7
Error: Invalid value for attribute y="{{0}}" bug.html:7
Error: Invalid value for attribute width="{{100}}" bug.html:7
Error: Invalid value for attribute height="{{100}}" bug.html:7

that apparently come from before the values are evaluated. They seem harmless, but are distracting (the real code generates a couple of thousand such errors, due to ng-repeat -- see http://dave.whipp.name/tutorial/anti-alias.html).

Most helpful comment

Actually, these days (AngularJS 1.5) you prepend your attributes with "ng-attr-", so:

<rect x="{{0}}" y="{{0}}" fill="red"/>

would become:

<rect ng-attr-x="{{0}}" ng-attr-y="{{0}}" fill="red"/>

All 14 comments

Is there a directive or another Angular technique to get around this error? Ng-cloak isn't sufficient.

Sadly, I've run into this exactly same issue. Presumably Chrome is "evaluating" the SVG before AngularJS has a chance to go in and mess with the DOM? (and rightfully cannot evaluate the string "<[ 100+v.row*100 ]>" down to a number with Angular's help).

I have had the same problem. Finally, I decided to write my own directives:

angular.module('yourmodule.directives', [])
    .directive('ngX', function() {
        return function(scope, element, attrs) {
            scope.$watch(attrs.ngX, function(value) {
                element.attr('x', value);
            });
        };
    })

Then, use ng-x attribute instead of x:

<circle ng-x="{{ x }}" y="0" r="5"></circle>

Finally got rid of annoying error messages.

That is an excellent solution. I will try it out. However, I wonder if I will have to create a whole bunch of directives for all the attributes that are affected.

Thanks.

Similarly to ngHref and ngSrc you can do

angular.forEach(['x', 'y', 'width', 'height'], function(name) {
  var ngName = 'ng' + name[0].toUpperCase() + name.slice(1);
  myModule.directive(ngName, function() {
    return function(scope, element, attrs) {
      attrs.$observe(ngName, function(value) {
        attrs.$set(name, value); 
      })
    };
  });
});

@mtiller I think so. I had to create a whole bunch of directives for all the attributes as well. But, as @rkirov suggested, you can use a loop to do that.

@rkirov I've never used attrs.$observe and attrs.$set. Are those better than using scope.$watch and element.attr? What are the differences?

Thanks for sharing that snippet, @rkirov, super helpful! Strange problem though: If you use that for "d" attributes as well, and try to set the value to empty string, resulting in elem.setAttribute("d", "") where elem is a path element, Chrome will give Error: Problem parsing d="" in the error console. But according to http://www.w3.org/TR/SVGTiny12/paths.html#DAttribute d="" is perfectly valid, and this is the way to disable rendering of the path, which sometimes you need. Anyone have any idea what's going on here? Is this a WebKit bug?

Is this a WebKit bug?

Yes.

It seems #1925 is a duplicate of this issue. I didn't notice this one, sorry.

There is a pending PR (https://github.com/angular/angular.js/pull/2061) that solves it using prefixes on the attributes that need late binding.

Unfortunately, as @skivvies pointed out, the problem about d="" is a WebKit bug...

Great solution vsirisanthana. I was having the same error "Expected moveto path command ('M' or 'm') angular" when drawing a line for the same reason on the attribute 'd'.

angular.module('yourmodule.directives', [])
.directive('ngD', function () {
    return function (scope, element, attrs) {
        scope.$watch(attrs.ngD, function (value) {
            element.attr('d', value);
        });
    };
});

Many thanks

Actually, these days (AngularJS 1.5) you prepend your attributes with "ng-attr-", so:

<rect x="{{0}}" y="{{0}}" fill="red"/>

would become:

<rect ng-attr-x="{{0}}" ng-attr-y="{{0}}" fill="red"/>

about the problem

Error: Invalid value for attribute x1="{{viewBox.x1}}"
Error: Invalid value for attribute y1="{{viewBox.y1}}"

you can replace the x1 and y1 byng-attr-x1 & ng-attr-y1

When using ngAttr, the allOrNothing flag of $interpolate is used, so if any expression in the interpolated string results in undefined, the attribute is removed and not added to the element

It's working with replacing d with ng-attr-d

Thanks peterjhart , it worked. I was wondering would it work if I created a link function and then processed the component through $onInit.

Was this page helpful?
0 / 5 - 0 ratings