angular ng-href and svg xlink

2019-01-22 01:54发布

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.

7条回答
我欲成王,谁敢阻挡
2楼-- · 2019-01-22 02:14

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

查看更多
啃猪蹄的小仙女
3楼-- · 2019-01-22 02:16

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

查看更多
地球回转人心会变
4楼-- · 2019-01-22 02:16

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
    />
查看更多
欢心
5楼-- · 2019-01-22 02:22

This took me more time than I would've wanted. Around 20-30 minutes.

If I understand correctly, any failed loading on image element will render that element useless in the future. I believe it's something similiar @GeekyMonkey is saying. If angular binding system has set xlink:href initially to null, Image element wont work anymore, even if we have valid value in the future.

Here is solution, notice how I have wrapped image element inside g element, using ng-if directive. That makes sure we will bind against image only when a correct value is available.

<g ng-if="vm.svgMap.background != null">
    <image
        ng-attr-xlink:href="{{vm.svgMap.background.image | trusted}}"
        ng-attr-width="{{vm.svgMap.background.width}}"
        ng-attr-height="{{vm.svgMap.background.width}}"

        xlink:href=""

        width="1"
        height="1"
        x="0"
        y="0"></image>
</g>

As others said, the order of attributes are important as well. To ensure that angularJS allows us to bind image element, we'll also have to trust that resource, I've done it through filter (it's the one in xlink:href attribute):

(function() {
    'use strict';

    angular.module('myTool').filter('trusted', TrustedFilter);

    function TrustedFilter($sce) {
        return function(url) {
            return $sce.trustAsResourceUrl(url);
        };
    };
}());
查看更多
Deceive 欺骗
6楼-- · 2019-01-22 02:30

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>
查看更多
倾城 Initia
7楼-- · 2019-01-22 02:31

I ran into this problem where I was using Ajax to load the svg spritesheet onto the page. If I had a on the page before the spritesheet was loaded, it would fail and would not resolve once the spritesheet was avaialble. Any added to the dom after the spritesheet was loaded were fine. I had to delay putting the items in the dom until after the spritesheet finished loading.

This only affected IOS. All other browsers didn't care about the order.

查看更多
登录 后发表回答