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

Und das ist meine RichtlinieEinschließlich SVG-Vorlage in AngularJS Richtlinie

module.directive('testrect', function() { 
    return { 
     restrict: 'E', 
     template: '<rect top="20" left="20" height="10" width="10" style="fill: #ff00ff" />', 
     replace: true 
    }; 
}); 

Aber das ist, was das Element wie im Browser suchen landet. Die Füllung zweimal ist kein Tippfehler.

<rect top="20" left="20" height="10" width="10" style="fill: #ff00ff;fill: #ff00ff"></rect> 

Hier ist ein jsfiddle http://jsfiddle.net/RG2CF/

Ist das, was ich versuche nicht möglich zu tun oder mache ich etwas falsch?

Dank

EDIT: Ich sollte hinzufügen, dass das Problem mit der Tatsache tun kann, dass die Vorlage svg rect Namespace statt svg xHTML, aber ich bin nicht sicher, wie ich diese oder Namespace erzwingen es zurück zu Svg, wenn das tatsächlich die Lösung ist.

+1

Ähnliche Diskussion hier: https://groups.google.com/forum/#!msg/angular/nP5JSuRuBMw/Wkv4DGMrtEUJ (scheint mir wie die Art, wie AngularJS mit Vorlagen nicht unterstützt SVG) –

+0

Danke dafür. Es ist sehr hilfreich. Ich habe den Eindruck, dass in der Google-Gruppe die meisten Angulars-Diskussionen stattfinden. – mkly

+1

"fill two" erscheint nicht, wenn die Winkelversion geändert wird. Es scheint spezifisch für 1.2.1. Vielleicht eingeführt von: https://github.com/angular/angular.js/commit/e1254b266dfa2d4e3756e4317152dbdbcabe44be#diff-a732922b631efed1b9f33a24082ae0dbR1595 – Olivvv

Antwort

4

Derzeit unterstützen SVG-Tags das dynamische Hinzufügen von Tags zumindest in Chrome nicht. Das neue rect wird nicht einmal angezeigt, auch nicht, wenn Sie es direkt mit dem DOM oder JQuery.append() hinzugefügt haben. 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" />'); 
}); 

Meine Vermutung ist, Sie gehen einige wirklich verrückt Verhalten zu sehen, egal, was Sie auf diese Weise tun. Es scheint, dass es ein Problem mit dem Browser ist und nicht so sehr Angular. I'd recommend reporting it to the Chromium project.

EDIT:

Es sieht aus wie es funktionieren könnte, wenn Sie es über das DOM 100% programmatisch hinzufügen: How to make Chrome redraw SVG dynamically added content?

+0

Ich hatte auch Probleme mit Firefox. Hast du das in deiner Firefox-Version tatsächlich gefunden? – mkly

+0

Ja, ich hatte auch Probleme mit dem in FF. –

+0

Wenn Sie möchten, dass dies in Zukunft funktioniert, kontaktieren Sie http://www.whatwg.org/ und/oder http://www.w3.org/html/wg/. Da gemäß den aktuellen Spezifikationen das obige rect nur ein HTMLUnknownElement im DOM wird. –

18

Angular 1.3 Update!

Meine ursprüngliche Antwort unten war bestenfalls ein riesiger Hack. Was Sie wirklich tun sollten, ist, auf Angular 1.3 zu aktualisieren und die Eigenschaft templateNamespace:'svg' auf Ihre Anweisung zu setzen. Schauen Sie unten auf @Josef Pfleger Antwort für ein Beispiel: Including SVG template in Angularjs directive


Alte Antwort

Falls jemand über diese in der Zukunft kommt, konnte ich einen Compiler-Funktion erstellen, die mit svg Vorlagen arbeitet . Es ist gerade ein bisschen hässlich, so dass Refactoring-Hilfe sehr geschätzt wird.

anschauliches Beispiel: 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'); 
     }; 
    } 
    }) 

Anwendungsfall:

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

Kumpel, nach 3 oder 4 Stunden googeln auf wie Svg-Elemente zu erstellen, war Ihr Service der erste Code, der für mich arbeitete. Danke Kumpel, du bist ein Live-Sparer! –

+0

Funktioniert es mit neuem Winkel 1.3.x? Es soll jetzt unterstützt werden –

+1

Es funktionierte für mich. –

3

ein innerHTML- Shim Verwendung für SVG (aka "innerSVG") stellt ein weiteres Stück des Puzzles, als Alternative zu Jason Mores aufschlussreiche Antwort. Ich finde, dass dieser innerHTML/innerSVG-Ansatz eleganter ist, da er keine spezielle Kompilierfunktion wie Jasons Antwort benötigt, aber er hat mindestens einen Nachteil: Er funktioniert nicht, wenn "replace: true" für die Direktive gesetzt ist.(Der innerSVG Ansatz wird hier diskutiert: 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 
}); 

Hier ist ein Plunker zu versuchen: http://plnkr.co/edit/nEqfbbvpUCOVC1XbTKdQ?p=preview

Der Grund, warum dies funktioniert, ist: SVG-Elemente innerhalb des XML-Namespace leben, http://www.w3.org/2000/svg, und als solche, sie muss entweder mit DOMParser() analysiert oder mit createElementNS() instanziiert werden (wie in Jasons Antwort), die beide den Namespace entsprechend einstellen können. Es wäre schön, wenn dieser Prozess der Verwendung von SVG so einfach wie HTML wäre, aber leider sind ihre DOM-Strukturen subtil unterschiedlich, was dazu führt, dass wir unsere eigene innerHTML-Shim bereitstellen müssen. Nun, wenn wir auch "replace: true" bekommen könnten, um damit zu arbeiten, wird das etwas Gutes sein!

BEARBEITEN: Die appendChild() - Schleife zu Workaround-Inhalten aktualisiert, die sich ändern, während die Schleife ausgeführt wird.

0

angle beachtet die Namespaces beim Erstellen von Elementen nicht (obwohl Elemente mit NamePages eines Parents geklont zu sein scheinen), so dass neue Anweisungen in <svg> ohne eine benutzerdefinierte Kompilierfunktion nicht funktionieren.

Etwas wie das Folgende kann das Problem umgehen, indem Sie die Erstellung der Dom-Elemente selbst vornehmen. Damit dies funktioniert, müssen Sie xmlns="http://www.w3.org/2000/svg" als Attribut für das Stammelement template haben und Ihre Vorlage muss gültiges XML sein (mit nur einem Stammelement).

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

Es gibt eine templateNamespace Eigenschaft, die Sie svg einstellen:

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

Hier ist eine link in der Dokumentation.

+1

Dies ist jetzt die richtige Antwort. Ich werde meine Antwort aktualisieren, um auf Ihre zu zeigen –

+1

Dies funktioniert seit 1.3.0-rc.5. – daerin

Verwandte Themen