Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

"invalid value for attribute" errors at startup for svg elements #1050

Closed
dwhipp opened this issue Jun 13, 2012 · 14 comments
Closed

"invalid value for attribute" errors at startup for svg elements #1050

dwhipp opened this issue Jun 13, 2012 · 14 comments

Comments

@dwhipp
Copy link

dwhipp commented Jun 13, 2012

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).

@geelen
Copy link

geelen commented Sep 8, 2012

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

@mtiller
Copy link

mtiller commented Sep 24, 2012

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).

@vsirisanthana
Copy link

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.

@mtiller
Copy link

mtiller commented Oct 21, 2012

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.

@rkirov
Copy link
Contributor

rkirov commented Oct 22, 2012

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); 
      })
    };
  });
});

@vsirisanthana
Copy link

@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?

lanterndev pushed a commit to getlantern/lantern-ui that referenced this issue Feb 4, 2013
@lanterndev
Copy link
Contributor

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?

@lanterndev
Copy link
Contributor

Is this a WebKit bug?

Yes.

@lrlopez
Copy link
Contributor

lrlopez commented Feb 24, 2013

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

There is a pending PR (#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...

lrlopez added a commit to lrlopez/angular.js that referenced this issue Feb 25, 2013
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
IgorMinar pushed a commit to IgorMinar/angular.js that referenced this issue Feb 27, 2013
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
@elenadelvallematute
Copy link

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

@peterjhart
Copy link

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"/>

@Berg-it
Copy link

Berg-it commented Oct 16, 2016

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

@AmelBattery
Copy link

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

lifthrasiir added a commit to devcat-studio/Jul8 that referenced this issue Mar 16, 2018
j8-attr-* 속성이 왜 필요하냐 하면 요런 문제 때문이다.
angular/angular.js#1050
앵귤러가 해결한 방법을 그대로 따라간다.
@bthornemail
Copy link

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.