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

E questa è la mia direttivaCompreso template SVG in direttiva Angularjs

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

Ma questo è ciò che l'elemento finisce per assomigliare nel browser. Il riempimento due volte non è un errore di battitura.

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

Ecco un jsfiddle http://jsfiddle.net/RG2CF/

è quello che sto cercando di fare non è possibile o sto facendo qualcosa di sbagliato?

Grazie

EDIT: Vorrei aggiungere che il problema potrebbe avere a che fare con il fatto che il modello svg rect è namespace a xhtml invece di SVG, ma io sono sicuro di come posso forzare questo o dello spazio dei nomi torna a svg, se questa è in realtà la soluzione.

+1

Discussioni simili qui: https://groups.google.com/forum/#!msg/angular/nP5JSuRuBMw/Wkv4DGMrtEUJ (mi sembra che il modo in cui AngularJS gestisce i modelli non supporta SVG) –

+0

Grazie. È molto utile Ho l'impressione che il gruppo di Google sia il luogo in cui si svolge gran parte della discussione di Angularjs. – mkly

+1

il "riempimento due volte" non appare quando viene modificata la versione angolare. Sembra specifico per 1.2.1. Forse introdotto da: https://github.com/angular/angular.js/commit/e1254b266dfa2d4e3756e4317152dbdbcabe44be#diff-a732922b631efed1b9f33a24082ae0dbR1595 – Olivvv

risposta

4

Attualmente i tag SVG non supportano l'aggiunta dinamica dei tag, almeno in Chrome. Non visualizza nemmeno il nuovo rect, né lo farebbe nemmeno se lo si aggiungesse direttamente al DOM o a 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" />'); 
}); 

La mia ipotesi è che si sta andando a vedere alcuni comportamenti davvero pazzesco, non importa quello che fai in questo modo. Sembra che si tratti di un problema con il browser e non tanto di Angular. I'd recommend reporting it to the Chromium project.

EDIT:

Sembra che potrebbe funzionare se si aggiunge tramite il DOM 100% a livello di codice: How to make Chrome redraw SVG dynamically added content?

+0

Ho avuto problemi anche con Firefox. Hai effettivamente trovato questo ha funzionato nella tua versione di Firefox? – mkly

+0

Sì, ho anche avuto problemi con l'in FF. –

+0

Se vuoi che funzioni in futuro, contatta http://www.whatwg.org/ e/o http://www.w3.org/html/wg/. Perché secondo le specifiche attuali il rect sopra diventerà semplicemente un HTMLUnknownElement nel DOM. –

18

angolare 1.3 Update!

La mia risposta originale di seguito è stata un attacco gigantesco al meglio. Quello che dovresti veramente fare è aggiornare ad Angular 1.3 e impostare la proprietà templateNamespace:'svg' sulla tua direttiva. Guardate in basso a @Josef Pfleger risposta per un esempio: Including SVG template in Angularjs directive


Vecchio risposta

Nel caso qualcuno si imbatte in futuro, sono stato in grado di creare una funzione di compilazione che funziona con i modelli SVG . È un po 'brutto in questo momento, quindi l'aiuto del refactoring è molto apprezzato.

vivo esempio: 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'); 
     }; 
    } 
    }) 

dei casi d'uso:

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

Amico, dopo 3 o 4 ore di google su come creare elementi svg, il tuo servizio è stato il primo codice che ha funzionato per me. Grazie amico, sei un risparmiatore live! –

+0

Funziona con il nuovo 1.3.x angolare? Dovrebbe essere supportato ora –

+1

Ha funzionato per me. –

3

Utilizzando uno spessore innerHTML per SVG (alias "innerSVG") fornisce un altro pezzo di questo puzzle, come alternativa a Jason più di risposta perspicace. Sento che questo approccio innerHTML/innerSVG è più elegante perché non richiede una funzione di compilazione speciale come la risposta di Jason, ma ha almeno un inconveniente: non funziona se "replace: true" è impostato sulla direttiva.(L'approccio innerSVG è discusso qui: 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 
}); 

Ecco un Plunker da provare: http://plnkr.co/edit/nEqfbbvpUCOVC1XbTKdQ?p=preview

Il motivo per cui questo funziona è: elementi SVG vivono all'interno del namespace XML, http://www.w3.org/2000/svg, e come tali, deve essere analizzato con DOMParser() o istanziato con createElementNS() (come nella risposta di Jason), entrambi i quali possono impostare lo spazio dei nomi in modo appropriato. Sarebbe bello se questo processo di utilizzo di SVG fosse facile come l'HTML, ma sfortunatamente le loro strutture DOM sono sottilmente diverse e ciò comporta la necessità di fornire il nostro shim innerHTML. Ora, se potessimo anche ottenere "replace: true" per lavorare con questo, sarà qualcosa di buono!

MODIFICA: aggiornato il ciclo appendChild() per modificare i contenuti mentre il ciclo si avvia.

0

angolare non presta attenzione agli spazi dei nomi durante la creazione di elementi (anche se gli elementi sembrano essere clonati con namepsace di un genitore), quindi le nuove direttive all'interno di <svg> non funzioneranno senza una funzione di compilazione personalizzata.

Qualcosa come il seguente può aggirare il problema creando la stessa dom elementi. Affinché questo funzioni, è necessario disporre di xmlns="http://www.w3.org/2000/svg" come attributo sull'elemento radice di template e il modello deve essere XML valido (con un solo elemento radice).

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

C'è una proprietà templateNamespace è possibile impostare per svg:

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

Ecco un link alla documentazione.

+1

Questa è la risposta corretta ora. Aggiornerò la mia risposta al punto –

+1

Questo funziona dal 1.3.0-rc.5. – daerin

Problemi correlati