Angular.js 在指令中更新 SVG 模板 [英] Angular.js updating SVG templates in directives
问题描述
不久前我问了Angular.js 在指令中渲染 SVG 模板",在这里我用 SVG 节点替换了 angular 在渲染模板时生成的 DOM 节点.我得到了一个为我回答的回复,但我意识到我丢失了 angular 的所有数据绑定.
查看 Plunkr(点击更新):http://plnkr.co/edit/HjOpqc?p=预览
如何用 SVG 节点替换这些 DOM 节点,并保持我的角度绑定不变?我尝试使用 $compile 使其工作(就像我使用常规 html 所做的那样),但它只是不起作用.
代码:
var svgNS = 'http://www.w3.org/2000/svg';app.directive('path', ngSvg('path'));app.directive('g', ngSvg('g'));功能ngSvg(类型){返回函数($超时,$编译){返回 {限制:'E',链接:函数(范围,EL,属性){//如果节点已经是svg,则跳过它们if (el[0].namespaceURI === svgNS) {返回;}//我希望下面的代码块能够工作,//但它不与 ng-repeat//var newAttr = {};//_.each(el[0].attributes, function(at) {//newAttr[at.nodeName] = at.value;//});//var path = makeNode(type, el, newAttr);//var parent = path.cloneNode(true);//$compile(parent)(scope);//var children = el.children();//$(parent).append(children);//$timeout(function() {//el.replaceWith(parent);//})//这适用于渲染,但不会更新 svg 元素//当点击更新时$超时(功能(){var newAttr = {};_.each(el[0].attributes, function(at) {newAttr[at.nodeName] = at.value;});var path = makeNode(type, el, newAttr);var parent = path.cloneNode(true);var children = el.children();$(parent).append(children);el.replaceWith(parent);});}}}}/* 使用给定的设置创建一个形状节点.*/函数makeNode(名称,元素,设置){//var ns = 'http://www.w3.org/2000/svg';var node = document.createElementNS(svgNS, name);for(设置中的var属性){var 值 = 设置[属性];if (value !== null && value !== null && !attribute.match(/\$/) &&(typeof value !== 'string' || value !== '')) {node.setAttribute(attribute, value);}}返回节点;}
这个问题在 Angular 1.3 中得到解决,这里是一些自定义 svg 指令的实现,具有您期望从 Angular 指令中获得的行为.现在对指令声明有一个特殊要求,如下 templateNamespace: 'svg'
另请注意,我覆盖了一些保留属性,例如 x
和 height
与
相关.为了保持对这些的更多控制,您可以利用 ng-attr
,例如 '<rect ng-attr-width="{{ ngWidth }}"/>
>
JSFiddle 链接
这里是一个自定义的
和
app.directive('ngRect', [function() {返回 {模板命名空间:'svg',替换:真的,模板:'<rect ng-attr-width="{{ ngWidth }}" ng-attr-height="{{ ngHeight }}" ng-attr-x="{{ ngX }}" ng-attr-y="{{ ngY }}" ng-click="ngRectClick()"/>',范围: {'ngHeight': '=','ngWidth': '='},链接:函数(范围,元素,属性){scope.ngRectClick = function() {控制台日志(元素);}}}}]);app.directive('ngCircle', [function () {返回 {模板命名空间:'svg',替换:真的,模板:'<circle ng-attr-cx="{{ ngCx }}" ng-attr-cy="{{ ngCy }}" ng-attr-r="{{ ngR }}" ng-attr-fill="{{ ngFill }}" ng-click="ngCircleClick()"/>',范围: {'ngCx': '=','ngCy': '=','ngR': '=','ngFill': '='},链接:函数(范围,元素,属性){scope.ngCircleClick = function() {控制台日志(元素);}}}}]);
A while ago I asked about "Angular.js rendering SVG templates in directives", where I was replacing the DOM nodes that angular makes when rendering templates, with SVG nodes. I got a response that answered it for me, but I realized that I lost all the databindings from angular.
See Plunkr (click update): http://plnkr.co/edit/HjOpqc?p=preview
How do I replace these DOM nodes with SVG nodes, and leave my angular bindings intact? I tried using $compile to make it work (as I've done with regular html), but its just not working.
code:
var svgNS = 'http://www.w3.org/2000/svg';
app.directive('path', ngSvg('path'));
app.directive('g', ngSvg('g'));
function ngSvg(type) {
return function($timeout, $compile) {
return {
restrict: 'E',
link: function(scope, el, attr) {
//skip nodes if they are already svg
if (el[0].namespaceURI === svgNS) {
return;
}
// I would expect the chunk of code below to work,
// but it does not with ng-repeat
// var newAttr = {};
// _.each(el[0].attributes, function(at) {
// newAttr[at.nodeName] = at.value;
// });
// var path = makeNode(type, el, newAttr);
// var parent = path.cloneNode(true);
// $compile(parent)(scope);
// var children = el.children();
// $(parent).append(children);
// $timeout(function() {
// el.replaceWith(parent);
// })
// this works for rendering, but does not update the svg elements
// when update is clicked
$timeout(function() {
var newAttr = {};
_.each(el[0].attributes, function(at) {
newAttr[at.nodeName] = at.value;
});
var path = makeNode(type, el, newAttr);
var parent = path.cloneNode(true);
var children = el.children();
$(parent).append(children);
el.replaceWith(parent);
});
}
}
}
}
/* 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(svgNS, 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;
}
This issue is solved in Angular 1.3 and here is an implementation of some custom svg directives with the behaviors you would expect from an Angular directive. There is now a special requirement is on the directive declaration as follows templateNamespace: 'svg'
Also notice I am overriding some reserved attributes, for example, x
and height
in regards to <rect/>
. To retain more control over these you can leverage ng-attr
as such '<rect ng-attr-width="{{ ngWidth }}" />
JSFiddle Link
Here is a custom <rect/>
and <circle/>
app.directive('ngRect', [function () {
return {
templateNamespace: 'svg',
replace: true,
template: '<rect ng-attr-width="{{ ngWidth }}" ng-attr-height="{{ ngHeight }}" ng-attr-x="{{ ngX }}" ng-attr-y="{{ ngY }}" ng-click="ngRectClick()"/>',
scope: {
'ngHeight': '=',
'ngWidth': '='
},
link: function (scope, elem, attrs) {
scope.ngRectClick = function() {
console.log(elem);
}
}
}
}]);
app.directive('ngCircle', [function () {
return {
templateNamespace: 'svg',
replace: true,
template: '<circle ng-attr-cx="{{ ngCx }}" ng-attr-cy="{{ ngCy }}" ng-attr-r="{{ ngR }}" ng-attr-fill="{{ ngFill }}" ng-click="ngCircleClick()"/>',
scope: {
'ngCx': '=',
'ngCy': '=',
'ngR': '=',
'ngFill': '='
},
link: function (scope, elem, attrs) {
scope.ngCircleClick = function() {
console.log(elem);
}
}
}
}]);
这篇关于Angular.js 在指令中更新 SVG 模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!