Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm working on a large project with SVG and angular.js, and need solid support for svg directive templates. Unfortunately when angular renders the templates, it creates DOM nodes, not SVG nodes. My current work around is to manage creating and deleting the nodes myself using jquery.svg, but its coming to it limits. Example: http://plnkr.co/edit/Xk8wM3?p=preview

I would like to have the directives element be the actual svg element, not some faux DOM element that doesn't really do anything. This will let me use ng-repeat and angular filters effectively.

Here is the plunkr that needs fixing: 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;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
172 views
Welcome To Ask or Share your Answers For Others

1 Answer

I have forked and updated your plunker to make it work here. Your function 'makeNode' was throwing error sometimes when the node in question was detached from document by angular for processing. Using a timeout of 0 ms does the trick of delaying the execution just enough to make the parent node available. Also, I am using the linking function to do all manipulation because the compile function executes only once per ng-repeat but we need it repeated number of times. You can read more about the difference between compile and linking functions under 'The difference between Compile and Link' section on Angular Directive page.

Here is the relevant code:

/* 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;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...