在 AngularJS 指令中渲染 SVG 模板 [英] Rendering SVG templates in AngularJS directives
问题描述
我正在处理一个包含 SVG 和 angular.js 的大型项目,需要对 svg 指令模板的可靠支持.不幸的是,当 angular 渲染模板时,它会创建 DOM 节点,而不是 SVG 节点.我目前的工作是使用 jquery.svg 自己管理创建和删除节点,但它的局限性.示例:http://plnkr.co/edit/Xk8wM3?p=preview>
我想让指令 element
成为实际的 svg 元素,而不是一些没有真正做任何事情的人造 DOM 元素.这将让我有效地使用 ng-repeat 和 angular 过滤器.
这是需要修复的 plunkr:http://plnkr.co/edit/BPvGjf?p=预览
html
javascript
var app = angular.module('plunker', []);app.controller('MainCtrl', function($scope) {$scope.squares = [{路径:M0,0L50,0L50,50L0,50z",颜色:'蓝色',订单:2}, {路径:M0,0L75,0L75,75L0,75z",颜色:'紫色',订单:1}];});app.directive('group', function($compile) {返回 {限制:'E',转置:真实,编译:函数(tElement,tAttr,transclude){var path = makeNode('g', tElement, tAttr);tElement.append(path.cloneNode(true));返回函数(范围,元素){transclude(范围,功能(克隆,innerScope){element.append($compile(clone)(innerScope));})}}}});app.directive('shape', function() {返回 {限制:'E',编译:函数(tElement,tAttr){var path = makeNode('path', tElement, tAttr);tElement.replaceWith(path.cloneNode(true));返回函数(范围,元素){}}}})/* 使用给定的设置创建一个形状节点.*/函数makeNode(名称,元素,设置){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';//我已经尝试使用这里的工厂来创建节点然后克隆//将其放入新的 svg,但由于 angular 节点是先创建子节点的,因此它不起作用var node = parent.ownerDocument.createElementNS(ns, name);for(设置中的var属性){var 值 = 设置[属性];if (value !== null && value !== null && !attribute.match(/\$/) &&(typeof value !== 'string' || value !== '')) {node.setAttribute(attribute, value);}}parent.appendChild(node);返回节点;}
我已经分叉并更新了您的 plunker 以使其正常工作 这里.当有问题的节点通过 angular 与文档分离以进行处理时,您的函数makeNode"有时会抛出错误.使用 0 毫秒的超时时间可以延迟执行,使父节点可用.另外,我使用链接函数来进行所有操作,因为编译函数每个 ng-repeat 只执行一次,但我们需要它重复多次.您可以在 Angular Directive 的编译和链接之间的区别"部分下阅读有关编译和链接函数之间区别的更多信息 页.
相关代码如下:
/* 使用给定的设置创建一个形状节点.*/函数makeNode(名称,元素,设置){var ns = 'http://www.w3.org/2000/svg';var node = document.createElementNS(ns, name);for(设置中的var属性){var 值 = 设置[属性];if (value !== null && value !== null && !attribute.match(/\$/) &&(typeof value !== 'string' || value !== '')) {node.setAttribute(attribute, value);}}返回节点;}
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;
}
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;
}
这篇关于在 AngularJS 指令中渲染 SVG 模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!