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

A to moja dyrektywaŁącznie szablon SVG w dyrektywie angularjs

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

Ale to, co kończy się element wygląda jak w przeglądarce. Wypełnienie dwukrotnie nie jest literówką.

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

Oto jsfiddle http://jsfiddle.net/RG2CF/

Czy to, co staram się robić nie możliwe lub robię coś źle?

Dzięki

EDIT: Dodam, że problem może mieć do czynienia z faktem, że rect szablon SVG przestrzeni nazw XHTML zamiast SVG, ale jestem pewien, w jaki sposób mogę zmusić tego lub nazw powraca do svg, jeśli to faktycznie jest rozwiązanie.

+1

Podobna dyskusja tutaj: https://groups.google.com/forum/#!msg/angular/nP5JSuRuBMw/Wkv4DGMrtEUJ (wydaje mi się, że sposób, w jaki AngularJS traktuje szablony, nie obsługuje SVG) –

+0

Dzięki za to. Jest bardzo pomocny. Odnoszę wrażenie, że grupa Google jest miejscem, w którym trwa większość dyskusji w Angularjs. – mkly

+1

"Wypełnij dwukrotnie" nie pojawia się, gdy zmieniona jest wersja kątowa. Wydaje się to charakterystyczne dla wersji 1.2.1. Może wprowadzone przez: https://github.com/angular/angular.js/commit/e1254b266dfa2d4e3756e4317152dbdbcabe44be#diff-a732922b631efed1b9f33a24082ae0dbR1595 – Olivvv

Odpowiedz

4

Obecnie tagi SVG nie obsługują dynamicznego dodawania tagów, przynajmniej w Chrome. Nie wyświetla nawet nowego recta, ani nawet, gdyby dodać go bezpośrednio do DOM lub 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" />'); 
}); 

Domyślam się masz zamiar zobaczyć kilka naprawdę szalone zachowania bez względu na to, co zrobić w ten sposób. Wygląda na to, że jest to problem z przeglądarką i nie tyle Angular. I'd recommend reporting it to the Chromium project.

EDIT:

Wygląda na to, że może działać, jeśli dodać go poprzez DOM 100% programowo: How to make Chrome redraw SVG dynamically added content?

+0

Miałem również problemy z Firefoksem. Czy faktycznie to działało w Twojej wersji Firefoksa? – mkly

+0

Tak, miałem też problemy z FF. –

+0

Jeśli chcesz, aby to działało w przyszłości, skontaktuj się z http://www.whatwg.org/ i/lub http://www.w3.org/html/wg/. Ponieważ zgodnie z aktualnymi specyfikacjami powyższy rect stanie się po prostu HTMLUnknownElement w DOM. –

18

kątowe 1.3 Update!

Moja oryginalna odpowiedź poniżej była w najlepszym wypadku gigantycznym hackerem. Naprawdę powinieneś dokonać aktualizacji do Angular 1.3 i ustawić właściwość templateNamespace:'svg' na swojej dyrektywie. Spójrz poniżej na @Josef Pfleger odpowiedzią na przykład: Including SVG template in Angularjs directive


Old Odpowiedź

W przypadku ktoś natknie to w przyszłości, udało mi się utworzyć funkcję kompilacji, który działa z szablonami svg . Jest teraz trochę brzydka, więc pomoc w refaktoryzacji jest bardzo doceniana.

żywo Przykład: sprawa 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'); 
     }; 
    } 
    }) 

zastosowanie:

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

Koleś, po 3 lub 4 godzinach googleingu, jak tworzyć elementy svg, twoja usługa była pierwszym kodem, który działał dla mnie. Dzięki kolego, jesteś wygaszaczem na żywo! –

+0

Czy działa z nowym kątem 1.3.x? Ma być obsługiwany teraz –

+1

To zadziałało dla mnie. –

3

Korzystanie podkładkę innerHTML dla SVG (aka "innerSVG") stanowi kolejny kawałek tej układanki, jako alternatywa Jason Morusa wnikliwa odpowiedź. Czuję, że to podejście innerHTML/innerSVG jest bardziej eleganckie, ponieważ nie wymaga specjalnej funkcji kompilacji, takiej jak odpowiedź Jasona, ale ma co najmniej jedną wadę: nie działa, jeśli w dyrektywie ustawiono "replace: true".(Podejście innerSVG omówiono tutaj: 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 
}); 

Oto Plunker spróbować: http://plnkr.co/edit/nEqfbbvpUCOVC1XbTKdQ?p=preview

Powodem tego działania jest: elementy SVG żyć w przestrzeni nazw XML, http://www.w3.org/2000/svg, i jako takie, musi zostać sparsowany z DOMParser() lub utworzony przy pomocy createElementNS() (jak w odpowiedzi Jasona), które mogą odpowiednio ustawić przestrzeń nazw. Byłoby miło, gdyby ten proces korzystania z SVG był tak prosty jak HTML, ale niestety ich struktury DOM są subtelnie różne, co powoduje potrzebę dostarczenia własnego podkładki innerHTML. Teraz, jeśli moglibyśmy uzyskać "replace: true" do pracy z tym, będzie to coś dobrego!

EDYCJA: Zaktualizowano pętlę appendChild(), zmieniając zawartość obejścia podczas działania pętli.

0

kątowy nie zwraca uwagi na przestrzenie nazw podczas tworzenia elementów (chociaż elementy wydają się być klonowane z nazwą rodzica rodzica), więc nowe dyrektywy wewnątrz <svg> nie będą działać bez niestandardowej funkcji kompilacji.

Coś jak poniżej może obejść ten problem, tworząc własne elementy domingu. Aby to działało, musisz mieć xmlns="http://www.w3.org/2000/svg" jako atrybut na głównym elemencie z template, a twój szablon musi być poprawnym XML (posiadającym tylko jeden element główny).

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

Istnieje właściwość templateNamespace można ustawić do svg:

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

Oto link do dokumentacji.

+1

To jest teraz poprawna odpowiedź. Zaktualizuję swoją odpowiedź do punktu na twoją odpowiedź –

+1

Działa to od wersji 1.3.0-rc.5. – daerin

Powiązane problemy