17

我正在使用 SVG 和 angular.js 开发一个大型项目,并且需要对 svg 指令模板的可靠支持。不幸的是,当 Angular 渲染模板时,它会创建 DOM 节点,而不是 SVG 节点。我目前的工作是使用 jquery.svg 自己管理创建和删除节点,但它会受到限制。示例:http ://plnkr.co/edit/Xk8wM3?p=preview

我想让指令element成为实际的 svg 元素,而不是一些没有真正做任何事情的人造 DOM 元素。这将让我有效地使用 ng-repeat 和角度过滤器。

这是需要修复的 plunkr:http ://plnkr.co/edit/BPvGjf?p=preview

html

<svg>
  <!--woot this one works-->
  <shape d="M0,0L250,0L250,250L0,250z" fill="green"></shape>

  <!--nesting directives doesn't work-->
  <group>
    <shape d="M0,0L150,0L150,150L0,150z" fill="red"></shape>
    <shape d="M0,0L100,0L100,100L0,100z" fill="orange"></shape>
  </group>

  <!--ng repeat doesn't work-->
  <shape d="{{square.path}}" fill="{{square.color}}" ng-repeat="square in squares | orderBy:'order'"></shape>
</svg>

javascript

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

app.controller('MainCtrl', function($scope) {
  $scope.squares = [{
    path: "M0,0L50,0L50,50L0,50z",
    color: 'blue',
    order: 2
  }, {
    path: "M0,0L75,0L75,75L0,75z",
    color: 'purple',
    order: 1
  }];
});

app.directive('group', function($compile) {
  return {
    restrict: 'E',
    transclude: true,
    compile: function(tElement, tAttr, transclude) {
      var path = makeNode('g', tElement, tAttr);

      tElement.append(path.cloneNode(true));

      return function(scope, element) {
        transclude(scope, function(clone, innerScope) {
          element.append($compile(clone)(innerScope));
        })
      }
    }
  }
});

app.directive('shape', function() {
  return {
    restrict: 'E',
    compile: function(tElement, tAttr) {
      var path = makeNode('path', tElement, tAttr);

      tElement.replaceWith(path.cloneNode(true));

      return function(scope, element) {

      }
    }
  }
})

/* Create a shape node with the given settings. */
function makeNode(name, element, settings) {
  var svg = $(element).parents('svg')[0];
  var parent = $(element).parent()[0];
  var factory = $('#factory')[0] || $('body').append('<svg id="factory"></svg>').find('#factory')[0];
  var ns = 'http://www.w3.org/2000/svg';

  // I've tried using the factory here to create the node then clone
  // it into the new svg, but since angular nodes are created child-first, it didn't work

  var node = parent.ownerDocument.createElementNS(ns, name);
  for (var attribute in settings) {
    var value = settings[attribute];
    if (value !== null && value !== null && !attribute.match(/\$/) &&
      (typeof value !== 'string' || value !== '')) {
      node.setAttribute(attribute, value);
    }
  }
  parent.appendChild(node);
  return node;
}
4

1 回答 1

16

我已经分叉并更新了您的 plunker 以使其在这里工作。当有问题的节点通过角度与文档分离以进行处理时,您的函数“makeNode”有时会引发错误。使用 0 毫秒的超时可以延迟执行以使父节点可用。另外,我使用链接函数来进行所有操作,因为编译函数每次 ng-repeat 只执行一次,但我们需要它重复多次。您可以在Angular Directive页面上的“编译和链接之间的区别”部分阅读更多关于编译和链接函数之间的区别。

以下是相关代码:

/* Create a shape node with the given settings. */
function makeNode(name, element, settings) {
  var ns = 'http://www.w3.org/2000/svg';
  var node = document.createElementNS(ns, name);
  for (var attribute in settings) {
    var value = settings[attribute];
    if (value !== null && value !== null && !attribute.match(/\$/) &&
      (typeof value !== 'string' || value !== '')) {
      node.setAttribute(attribute, value);
    }
  }
  return node;
}
于 2013-10-25T21:47:16.770 回答