AngularJS ng-href and svg xlink

18,234

Solution 1

You can use ng-attr-<some attribute>

ng-attr-xlink:href="{{xxx}}" works for me.


Note that you also need an empty xlink:href="" as initial value. – Derek Hsu

Solution 2

If, like me, you're looking for a way to add images to svg, you can do so adding:

xlink:href="" ng-href="{{ foo }}"

Example:

http://jsbin.com/sigoleya/1/edit?html,js,output

Where I found the solution:

https://github.com/angular/angular.js/issues/7697

Solution 3

I ran into a similar problem when trying to output a value for xlink:href that's tied to the model. Based on the user's chosen <option> in a <select> control, I was trying to show a dynamic SVG icon via the xlink:href attribute of the <use> element.

I found a thread about this in the GitHub Issues for AngularJS. Based on the discussion there, it appears that because a viable workaround exists, they've effectively tabled a fix by moving it to the Backlog milestone.

What ultimately worked for me was inspired by this JSBin:

http://jsbin.com/sigoleya/1/edit?html,js,output

Here's the code I used in my template:

<svg class="icon" data-ng-class="category.iconName">
  <use xlink:href="" data-ng-href="{{'#' + category.iconName}}">
</svg>

Given a category.iconName of icon-music, for example, Angular sets the xlink:href dynamically to #icon-music, which references the <svg id="icon-music"> element further up on the same page.

As others have noted, what's key is setting a blank xlink:href="" attribute on the element where you call the ngHref directive. Attribute order does not seem to matter. Using ng-attr-xlink:href="{{xxx}}" (as mentioned in Derek Hsu's answer) did not work for me.

All of this assumes Angular 1.3.36.

Solution 4

I solved the same problem with the following modules:

Module for SVGs:

var app = angular.module('Svgs', []);

angular.forEach([
    { ngAttrName: 'ngXlinkHref', attrName: 'xlink:href' },
    { ngAttrName: 'ngWidth', attrName: 'width' },
    { ngAttrName: 'ngHeight', attrName: 'height' }
], function (pair) {

    var ngAttrName = pair.ngAttrName;
    var attrName = pair.attrName;

    app.directive(ngAttrName, function (IeHelperSrv) {

        return {

            priority: 99,

            link: function (scope, element, attrs) {

                attrs.$observe(ngAttrName, function (value) {

                    if (!value) return;

                    attrs.$set(attrName, value);
                    if (IeHelperSrv.isIE) element.prop(attrName, value);
                });
            }
        };
    });
});

Module for IE detection:

angular.module('IeHelper', []).factory('IeHelperSrv', function () {

    return {
        isIE: checkForIE.isIE,
    }
});

var checkForIE = {
    init: function () {
        this.isIE = (navigator.userAgent.indexOf('MSIE') != -1);
    }
};

checkForIE.init();

HTML:

<!-- image has initial fake source, width and height to force it to render -->
<image xlink:href="~/Content/Empty.png" width="1" height="1"
    ng-xlink-href="{{item.imageSrc}}"
    ng-width="{{item.width}}" ng-height="{{item.height}}"
    ng-cloak
    />

Solution 5

For anyone else having this problem due to Angular/Angular UI Router in HTML5 mode, I came up with a straightforward fix to enable svg sprite icons to work with their xlink:href attribute and the tag.

Gist is here: https://gist.github.com/planetflash/4d9d66e924aae95f7618c03f2aabd4a3

app.run(['$rootScope', '$window', function($rootScope, $window){
 $rootScope.$on('$locationChangeSuccess', function(event){
    $rootScope.absurl = $window.location.href;
});

<svg><use xlink:href="{{absurl+'#svgvID'}}"></use></svg>
Share:
18,234
Leon Radley
Author by

Leon Radley

I am the CTO at wec360.com. Scandinavias leading visualization platform.

Updated on June 06, 2022

Comments

  • Leon Radley
    Leon Radley about 2 years

    I'd like some input on using xml namespaced attributes with angular.

    The problem is angular comes with a couple of directives to handle writing attributes such as href and src when angular has parsed the expresssions (otherwise the browser will try to load {{mymodel.myimage}} as a url)

    https://github.com/angular/angular.js/blob/master/src/ng/directive/booleanAttrs.js#L329

    The problem I'm facing is that I'm using angular to output svg together with D3 and since angular doesn't have a way to output xlink:href I was stuck.

    I created a custom directive that outputs xlink:href

    app.directive('ngXlinkHref', function () {
      return {
        priority: 99,
        restrict: 'A',
        link: function (scope, element, attr) {
          var attrName = 'xlink:href';
          attr.$observe('ngXlinkHref', function (value) {
            if (!value)
              return;
    
            attr.$set(attrName, value);
          });
        }
      };
    });
    

    Full demo: http://plnkr.co/edit/cMhGRh

    But it seems that if I don't manually add xlink:href to the element, the svg image will not render.

    Any suggestions on how to best handle xml namespaces / svg together with angular would be greatly appreciated.

  • coblr
    coblr over 10 years
    When the xlink:href wasn't working for me, I had to re-read this post. The key here is that YOU NEED A FAKE VALUE TO START WITH. Without it, the image will not render even with the correct attribute. Leaving this comment to help other TL;DR dummies like me. Thanks Danny!
  • Reaper
    Reaper over 10 years
    @fractalspawn You are welcome. I was going to add this comment to the answer, then I saw I already put this in a comment above the HTML :-)
  • Siddharth
    Siddharth almost 10 years
    This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post - you can always comment on your own posts, and once you have sufficient reputation you will be able to comment on any post.
  • Derek Hsu
    Derek Hsu almost 10 years
    @Siddharth, Actually it does. The question is not simply about custom directives. Indeed, @Leon wanted some input on using xml namespaced attributes with angular and suggestions on how to best handle xml namespaces / svg together with angular. To resolve his problem, he was even writing custom directives. I am pointing out that custom directives are not necessary. Angularjs already has ng-attr which does the job for him.
  • sheriffderek
    sheriffderek almost 10 years
    How about a fiddle to prove this? It's not working for me... <svg><use ng-attr-xlink:href="#icon-{{notification.type}}"></use></svg‌​>
  • Derek Hsu
    Derek Hsu almost 10 years
    @sheriffderek, here you go angularjs ng-attr example. Note that you also need an empty xlink:href="" as initial value.
  • sheriffderek
    sheriffderek almost 10 years
    I was hoping to use it with <use> -- so that I can target pieces of a sprite. I don't really have any reason to put an image in an svg.
  • sheriffderek
    sheriffderek almost 10 years
    AH --- actually --- I didn't read it properly. Here is a codepen where I have it working the way I want. Why don't you put the actual directive code in the answer? Then it would be correct. : ) - codepen.io/sheriffderek/pen/EdCDr THANKS!
  • eCommerce Guru
    eCommerce Guru almost 10 years
    +1, This works for me, but it's VERY picky. You meed the empty xlink:href="" AFTER the dynamic ng-attr-xlink:href. Also, I don't think this works for earlier versions of Angular.
  • Mattijs
    Mattijs over 7 years
    this is pretty brilliant. Thanks!
  • Mohamed Hussain
    Mohamed Hussain about 5 years
    thank u so much it works for me...ng-attr-xlink:href="{{model}}" didnt work for me.....
  • Nitsan Baleli
    Nitsan Baleli over 4 years
    coffeescript: <use xlink:href="" ng-attr-href="\#{{$ctrl.name}}"></use>