简体   繁体   中英

Angular + SVG: how to dynamically load template from string

In Angular, it's possible to use SVG as component template. I wonder if there is a way to convert a SVG content (string loaded from server) into a component template, including the possibility to bind some properties to it (ie, <tspan [attr.color]="textColor"...>{{myText}}</tspan> ).

I've made some tests and all that I got was to load and render the SVG content, but the binds are not working. Instead, they are rendered as strings.

I know that if put the SVG content in a file and reference it in component's templateUrl setting, it works. For example:

...
@Component({
   templateUrl: './content.svg'
})
export class MyComponent {
   ...
}
...

I've already tested it. However, I need to load the SVG from a server (it cannot be a static file or hard coded in source-code).

Can anyone help me?

Update

Just to clarify a little bit...

Consider the following component:

@Component({
    selector: 'my-component',
    template: `<div [innerHtml]="mySvg"></div>`
})
export class MyComponent {
    textColor: string;
    myText: string;
    mySvg: string;

    constructor() {
        this.textColor = '#ff0000';
        this.myText = 'Just testing';
    }

    loadSvg() {
        // here goes some logic to request the SVG string from server
        // ...
       this.mySvg = response;
    }
}

Now, consider that the server returned the following string:

<svg>
    <tspan [attr.color]="textColor">{{myText}}</tspan>
</svg>

I would like the SVG to be rendered and the interpolations to work as well (making it possible to change the text and color).

Is it possible?

If you stick to native standard Javascript template literal notation: ${x}

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

 `string ${property} string string ${property} string` 

You can create a tagged template function that does all the replacing of property values for you

Here the template string is the innerHTML , properties as attributes

When you make up your own template syntax (or use 3rd party) You will have to add a lot more boilerplate code or libraries to parse that String

 <svg-from-template cx="30" cy="30"> <rect x='0' y='0' width='100%' height='100%' fill='${bgcolor}'></rect> <circle cx='${cx}' cy='${cy}' r='10%' fill='${fill}'></circle> </svg-from-template> <svg-from-template bgcolor="lightgreen" cx="70" cy="70" fill="green"> <rect x='0' y='0' width='100%' height='100%' fill='${bgcolor}'></rect> <circle cx='${cx}' cy='${cy}' r='20%' fill='${fill}'></circle> </svg-from-template> <script> customElements.define( "svg-from-template", class extends HTMLElement { connectedCallback() { function parseTemplate(templateString, templateData = {}) { return new Function( // create Function AND return the executed function result "templateProps", [ "let f = ( " + Object.keys(templateData).join(",") + " ) =>", "`" + templateString + "`", "return f(...Object.values(templateProps))", ].join("\n") )(templateData); // execute tagged template function } setTimeout(() => { // wait till all innerHTML is parsed by the Browser let templateData = [...this.attributes].reduce((acc, attr) => { acc[attr.name] = attr.value; // copy element attributes to {} object return acc; }, { /*default object*/ bgcolor: 'pink', fill: 'red' }); let templateString = this.innerHTML; // OR read from file/server/universe this.innerHTML = "<svg mlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>" + parseTemplate(templateString, templateData) + "</svg>"; }); } } ); </script> <style> svg { width: 180px; /* SO snippet window height */ } </style>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM