2012-11-30 105 views
19
<div ng-app="testrectApp"> 
<svg> 
    <rect height="10" width="10" style="fill: #00ff00" /> 
    <testrect /> 
</svg> 
</div> 

这是我的指令包括SVG模板

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,如果这实际上是解决方案。

+1

类似的讨论在这里:https://groups.google.com/forum/#!msg/angular/nP5JSuRuBMw/Wkv4DGMrtEUJ(在我看来,AngularJS处理模板的方式不支持SVG) –

+0

感谢。这非常有帮助。我觉得Google小组是大多数Angularjs讨论的地方。 – mkly

+1

当角度版本更改时,不会出现“填充两次”。它似乎是特定于1.2.1。也许通过引入:https://github.com/angular/angular.js/commit/e1254b266dfa2d4e3756e4317152dbdbcabe44be#diff-a732922b631efed1b9f33a24082ae0dbR1595 – Olivvv

回答

4

目前SVG标签不支持动态添加标签,至少在Chrome中是如此。它甚至不显示新矩形,即使直接使用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?

+0

我也有Firefox的问题。你真的发现这在你的Firefox版本中工作吗? – mkly

+0

是的,我在FF中也遇到了问题。 –

+0

如果您希望将来可以使用,请联系http://www.whatwg.org/和/或http://www.w3.org/html/wg/。因为根据当前的规范,上面的矩形只会成为DOM中的HTMLUnknownElement。 –

18

角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 
    }; 
}); 
+0

老兄,在如何创建svg元素的3或4个小时后,您的服务是第一个为我工作的代码。感谢队友,你是一位现场救星! –

+0

它是否适用于新的角度1.3.x?它应该现在支持 –

+1

它为我工作。 –

3

使用的SVG(又名 “innerSVG”)的innerHTML的垫片提供了另一块这个难题,作为替代杰森更多的有见地的答案。我觉得这个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垫片。现在,如果我们也可以用“取代:真”来解决这个问题,那就太好了!

编辑:更新了appendChild()循环,以便在循环运行时更改变通办法内容。

0

angular在创建元素时不会关注名称空间(尽管元素似乎是用父级的namepsace克隆的),所以<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); 
26

有一个templateNamespace属性,你可以设置为svg

module.directive('testrect', function() { 
    return { 
     restrict: 'E', 
     templateNamespace: 'svg', 
     template: '<rect .../>', 
     replace: true 
    }; 
}); 

这里是一个link的文档。

+1

这是现在正确的答案。我会更新我的答案,指向你的 –

+1

这是从1.3.0-rc.5开始的。 – daerin