yankee
yankee

Reputation: 40860

Why do my angular <img ng:src .../> behave different depending on the browser?

My angular page which I reduced to a to a minimal example here I tried three different ways to show the same img (just a red rectangle) in the "img"-Page:

In Firefox all three images are displayed just fine.

In Opera the page shows only the later two images.

Chromium refuses to even load the page at all (it says "Error: An invalid or illegal string was specified." on the console). (It works if I remove the XML-Style img).

Why is this happening? Since I believe in XML my preference would be to get the XML-Style Parameter to work in all browsers. Note that I don't care about Internet Explorer.

Archive with my (broken) project: http://cipher-code.de/tmp/angular.zip

Upvotes: 2

Views: 1112

Answers (2)

Michal Charemza
Michal Charemza

Reputation: 27062

Chrome appears to be quite strict when injecting fragments of XML into an XML document, with regards to namespaces. Your partial of images

<section>
  <h1>Img</h1>
  <p>With ng:src: <img ng:src="{{img}}" /></p>
  <p>With ng-src: <img ng-src="{{img}}" /></p>
  <p>With src: <img src="{{img}}" /></p>
  <p>As link: <a href="{{img}}">Go to img</a></p>
</section>

is using the ng namespace, but it's not declared. The following fixes it in Chrome

<section xmlns:ng="http://angularjs.org">
  <h1>Img</h1>
  <p>With ng:src: <img ng:src="{{img}}" /></p>
  <p>With ng-src: <img ng-src="{{img}}" /></p>
  <p>With src: <img src="{{img}}" /></p>
  <p>As link: <a href="{{img}}">Go to img</a></p>
</section>

Alas not sure why it then doesn't work in Opera.

Edit:

After a bit more testing, it looks like Opera can't quite cope with attributes src and ng:src on the same element. A solution is to roll your own ngSrc directive, called something like myImgsrc,

myModule.directive('myImgsrc', function() {
  return {
    link: function(scope, element, attrs) {
      attrs.$observe('myImgsrc', function(src) {
        attrs.$set('src', src);
      });
    }
  }
});

Used as:

<img my:imgsrc="{{img}}" />

(Remembering to properly declare the namespace xmlns:my="http://mydomain.com" in both the document and the partial, so as not to break Chrome)

Upvotes: 1

Steve Kl&#246;sters
Steve Kl&#246;sters

Reputation: 9457

The XHTML spec outlines this algorithm that Chrome does not implement fully. Chrome returns the error you observe when the XML is not well formed, which is true when the following step in the algorithm is not followed:

If there is a context element, feed the parser just created the string corresponding to the start tag of that element, declaring all the namespace prefixes that are in scope on that element in the DOM, as well as declaring the default namespace (if any) that is in scope on that element in the DOM.

The result of not following this step means you have to set xmlns:ng on the root element of the fragment you are appending (or technically, any element or parent thereof that uses the ng namespace)

Edit after further discussion in comments: element.setAttribute on Opera doesn't do anything if the attribute doesn't exist. AngularJS has a compatibility fix for this for Internet Explorer. Enabling this fix for Opera solves the issue on Opera as well. A bug is issued with AngularJS here. Internet Explorer and Opera seem to misinterpret the spec on this.

Upvotes: 4

Related Questions