<div ng-app="testrectApp">
<svg>
<rect height="10" width="10" style="fill: #00ff00" />
<testrect />
</svg>
</div>
这是我的指示
module.directive('testrect', function() {
return {
restrict: 'E',
template: '<rect top="20" left="20" height="10" width="10" style="fill: #ff00ff" />',
replace: true
};
});
但这就是元素最终看起来像在浏览器中。填充两次不是拼写错误。
<rect top="20" left="20" height="10" width="10" style="fill: #ff00ff;fill: #ff00ff"></rect>
这是一个jsfiddle http://jsfiddle.net/RG2CF/
我想做的不可能或我做错了什么?
由于
编辑:我应该补充一点,这个问题可能与模板svg rect命名为xhtml而不是svg这一事实有关,但我不确定如何强制这个或命名空间它回到svg,如果这实际上是解决方案。答案 0 :(得分:28)
您可以将templateNamespace
属性设置为svg
:
module.directive('testrect', function() {
return {
restrict: 'E',
templateNamespace: 'svg',
template: '<rect .../>',
replace: true
};
});
以下是文档的link。
答案 1 :(得分:18)
Angular 1.3更新!
我在下面的原始答案充其量只是一个巨大的黑客。你应该做的是更新Angular 1.3并在你的指令上设置templateNamespace:'svg'
属性。请看下面的@Josef Pfleger答案:Including SVG template in Angularjs directive
旧答案
如果将来遇到这种情况,我可以创建一个与svg模板一起使用的编译功能。它现在有点难看,所以非常感谢重构帮助。
直播示例:http://plnkr.co/edit/DN2M3M?p=preview
app.service('svgService', function(){
var xmlns = "http://www.w3.org/2000/svg";
var compileNode = function(angularElement){
var rawElement = angularElement[0];
//new lines have no localName
if(!rawElement.localName){
var text = document.createTextNode(rawElement.wholeText);
return angular.element(text);
}
var replacement = document.createElementNS(xmlns,rawElement.localName);
var children = angularElement.children();
angular.forEach(children, function (value) {
var newChildNode = compileNode(angular.element(value));
replacement.appendChild(newChildNode[0]);
});
if(rawElement.localName === 'text'){
replacement.textContent = rawElement.innerText;
}
var attributes = rawElement.attributes;
for (var i = 0; i < attributes.length ; i++) {
replacement.setAttribute(attributes[i].name, attributes[i].value);
}
angularElement.replaceWith(replacement);
return angular.element(replacement);
};
this.compile = function(elem, attrs, transclude) {
compileNode(elem);
return function postLink(scope, elem,attrs,controller){
// console.log('link called');
};
}
})
用例:
app.directive('ngRectAndText', function(svgService) {
return {
restrict: 'E',
template:
'<g>'
+ '<rect x="140" y="150" width="25" height="25" fill="red"></rect>'
+ '<text x="140" y="150">Hi There</text>'
+ '</g>'
,
replace: true,
compile: svgService.compile
};
});
答案 2 :(得分:4)
目前,SVG代码不支持动态添加代码,至少在Chrome中是这样。它甚至不显示新的rect,即使你直接用DOM或JQuery.append()添加它也不会。 Have a look at just trying to do it with JQuery
// this doesn't even work right. Slightly different error than yours.
$(function() {
$('svg').append('<rect top="20" left="20" height="10" width="10" style="fill: #ff00ff" />');
});
我的猜测是,无论你以这种方式做什么,你都会看到一些非常疯狂的行为。这似乎是浏览器的一个问题,而不是Angular。 I'd recommend reporting it to the Chromium project
编辑:
如果您通过DOM 100%以编程方式添加它,它看起来可能会有效:How to make Chrome redraw SVG dynamically added content?
答案 3 :(得分:3)
使用innerHTML shim for SVG(又名“innerSVG”)提供了另一个难题,作为Jason More的深刻回答的替代方案。我觉得这个innerHTML / innerSVG方法更优雅,因为它不需要像Jason的答案那样的特殊编译函数,但它至少有一个缺点:如果在指令上设置了“replace:true”,它就不起作用。 (这里讨论了innerSVG方法:Is there some innerHTML replacement in SVG/XML?)
// Inspiration for this derived from a shim known as "innerSVG" which adds
// an "innerHTML" attribute to SVGElement:
Object.defineProperty(SVGElement.prototype, 'innerHTML', {
get: function() {
// TBD!
},
set: function(markup) {
// 1. Remove all children
while (this.firstChild) {
this.removeChild(this.firstChild);
}
// 2. Parse the SVG
var doc = new DOMParser().parseFromString(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">'
+ markup
+ '</svg>',
'application/xml'
);
// 3. Import the parsed SVG and grab the childNodes
var contents = this.ownerDocument.importNode(doc.documentElement, true).childNodes;
// 4. Append the childNodes to this node
// Note: the following for loop won't work because as items are appended,
// they are removed from contents (you'd end up with only every other
// element getting appended and the other half remaining still in the
// original document tree):
//for (var i = 0; i < contents.length; i++) {
// this.appendChild(contents[i]);
//}
while (contents.length) {
this.appendChild(contents[0]);
}
},
enumerable: false,
configurable: true
});
这是一个尝试的Plunker:http://plnkr.co/edit/nEqfbbvpUCOVC1XbTKdQ?p=preview
这样做的原因是:SVG元素存在于XML命名空间http://www.w3.org/2000/svg中,因此,它们必须使用DOMParser()进行解析或使用createElementNS()进行实例化(如Jason的回答),两者都可以适当地设置命名空间。如果这个使用SVG的过程像HTML一样容易,那就太好了,但不幸的是它们的DOM结构略有不同,导致需要提供我们自己的innerHTML垫片。现在,如果我们也可以使用“替换:true”来处理这个问题,那将是件好事!
编辑:更新了appendChild()循环以解决在循环运行时更改内容的问题。
答案 4 :(得分:0)
angular在创建元素时不会关注命名空间(尽管元素似乎是用父元素的名称来克隆的),因此如果没有自定义编译函数,<svg>
中的新指令将无法工作。
以下内容可以通过创建dom元素本身来解决问题。为此,您需要xmlns="http://www.w3.org/2000/svg"
作为template
根元素的属性,并且您的模板必须是有效的XML(只有一个根元素)。
directiveGenerator = function($compile) {
var ret, template;
template = "<g xmlns=\"http://www.w3.org/2000/svg\"> +
"<rect top=\"20\" left=\"20\" height=\"10\" width=\"10\" style=\"fill: #ff00ff\" />" +
"</g>";
ret = {
restrict: 'E',
compile: function(elm, attrs, transclude) {
var templateElms;
templateElms = (new DOMParser).parseFromString(template, 'application/xml');
elm.replaceWith(templateElms.documentElement);
return function(scope, elm, attrs) {
// Your link function
};
}
};
return ret;
};
angular.module('svg.testrect', []).directive('testrec', directiveGenerator);