AngularJS: включение элементов SVG в директивы

Я заинтересован в создании составного SVG путем создания многократно используемых графических элементов, заключенных в директивы Angular.js. Например, я мог бы иметь:

<div ng-app="svgApp">
  <canvas>
    <drawing ng-repeat="i in [1,2,3]" cy="{{i * 40}}"></drawing>
  </canvas>
</div>

где я определяю следующие директивы:

  .directive('canvas', function () {
    return {
      template: '<svg height=200 width=100 ng-transclude></svg>',
      restrict: 'E',
      replace: true,
      transclude: true
    };
  })

  .directive('drawing', function () {
    return {
      template: '<circle cx=50 r=15></circle>',
      restrict: 'E',
      replace: true
    };
  })

Проблема заключается в том, что элементы SVG не отображаются должным образом. Кажется, здесь есть подсказка , в другом вопросе Stackru, что это главным образом потому, что узлы SVG не созданы должным образом в Angular.js.

Пройдя дальше, я нашел это решение, которое включает использование вспомогательной функции для замены соответствующих элементов DOM на правильно созданные SVG-узлы, а именно:

  .value('createSVGNode', function(name, element, settings) {
      var namespace = 'http://www.w3.org/2000/svg';
      var node = document.createElementNS(namespace, name);
      for (var attribute in settings) {
        var value = settings[attribute];
        if (value !== null && !attribute.match(/\$/) && (typeof value !== 'string' || value !== '')) {
            node.setAttribute(attribute, value);
        }
      }
      return node;
  });

Тем не менее, казалось нежелательным, чтобы мне приходилось использовать это везде, и я хотел, чтобы обходной путь был максимально локальным для ошибки, пока она не будет исправлена.

Мой вопрос заключается в том, будет ли следующее решение разумным:

angular.forEach(['circle', ...], function (svgElem) {

  svgModule

    .directive(svgElem, function (createSVGNode) {
      return {
        restrict: 'E',
        link: function(scope, element, attrs) {
          var node = createSVGNode(svgElem, element, attrs);
          angular.element(node).append(element[0].childNodes);
          element.replaceWith(node);
        }
      };
    });

});

И это работает в Plunker!

Допустимо ли мне переопределять существующие директивы SVG-элемента таким образом?

1 ответ

Решение

Выше приведен возможный подход, если вы не можете переместить свой код к активной подсказке разработки AngularJS 1.3, где решена проблема включения элементов другого пространства имен (например, SVG или MathML).

Связанный Plunker демонстрирует, как обновить ваш код с исправлением. Ключ является добавлением нового ключа "templateNamespace" в объект определения директивы:

  .directive('svgInternal', function () {
    return {
      templateNamespace: 'svg',
      template: '<g><rect height="25" width="25" /><text x="30" y="20">Hello, World</text></g>',
      restrict: 'E',
      replace: true
    };
  })

где следующая разметка демонстрирует используемую директиву типа SVG:

  <svg height="30">
    <svg-internal></svg-internal>
  </svg>

Изменить: "тип" был изменен на "templateNamespace" с 1.3.beta.19.

Другие вопросы по тегам