Wednesday, September 17, 2014

Working with SVG in Ember

(Tomster constructiony logo thing... unsure what artist to credit)

At the time of this post, working with SVG in Ember can be a PITA to say the least. At Netflix, I'm working on an Ember application that is at least 90% SVG, and I've hit a lot, if not all, of the pain points you might have before finding this post. Hopefully someone else finds this helpful.
The issues faced in Ember with SVG are the same issues faced by most frameworks. In fact, as of right now (now meaning while I'm typing this), there are no stable versions of JavaScript "frameworks" that fully support SVG properly. Angular, Ember, React and Polymer all have some pain around handling SVG. Some of them attempt to handle it, React for example, but there are certain cases where it will still break.

First Problem - Partials getting parsed incorrectly

This happens when the root of a template is an SVG element. Ember, like JQuery, React, and in some cases Angular, use a "wrap map" technique to parse HTML strings.  This essentially creates a div tag, puts your string wrapped in some other tags into the div's innerHTML, then pulls out the child nodes you required. That's a simplified version of the technique, and I can go over that in another post. The "map" comes in when trying to determine what to "wrap" the string you provided with. For example wrapping a <td> with <table><tbody><tr>.

The problem with this technique is that SVG elements, being in a different namespace than HTML elements, will not be created properly if they're not inside of an <svg> tag. And all 50 or so SVG tags are simply not in the wrap map.

Second Problem - CSS Classes not being applied properly

Under the hood Ember uses JQuery to add and remove classes from elements. The problem here is that JQuery doesn't support SVG. I could go on a rant here about how I don't like JQuery, but meh.  The reason JQuery doesn't work to add and remove classes from SVG elements is that JQuery tries to do so by setting element.className to a string. In SVG, element.className is an SVGAnimatedString, and you must set baseVal to the string instead. But better still, if you're in IE8 and newer the "modern browsers" JQuery 2.x and higher purports to target, you can just use classList to add and remove classes. Alternatively, setting class via setAttribute('class', 'whatever') will also work with SVG.

The Solution (for now): 1.7.0 beta.5 with a script

Thanks to Stefan Penner, I was able to push through a PR to Ember that exposes the wrap map used in Handlebars via Ember._metamorphWrapMap. This allowed me to create a patch script that updates the wrap map with the appropriate elements for SVG. That same patch also updates JQuery's addClass and removeClass to use classList internally, which might break for really old browsers. (Also, as of right now I'm not supporting passing functions to those methods but feel free to submit a PR if you need that feature).

  1. ember 1.7.0-beta.5 has the wrap map exposure. (via bower: bower install ember#1.7.0-beta.5)
  2. ember-handlebars-svg is a script I made to update the wrap map and fix class manipulation. (via bower: bower install ember-handlebars-svg)

After that everything should "just work". No other JQuery patches or anything are required unless you're trying to use JQuery for animation or something, in which case, I'd recommend ditching that and using D3. There are still a few places you could have a problem, specifically if you try to use an svg <a> tag as a root element to a template, and that's unavoidable because of the wrap map approach, but other than that, you *should* be okay. (should, haha).

The FUTURE!!!!

In short, the future of SVG in Ember looks 100% awesome. And the answer is "HTMLBars".

I've talked about this issue at length with Stefan Penner, Kris Seldan, Erik Bryn and Matthew Beale and how it's going to be solved in the future of Ember. Currently SVG in 1.8.0 with metal-views is broken again. But not to worry: HTMLBars and metal-views will allow for a more complete solution to this problem that will use namespace tracking instead of clumsy wrap maps to provide functionality to properly create elements in each namespace. This means it will work 100% of the time, where a wrap map will still have some issues (for example both HTML and SVG have an <a> tag).

Stay up to date

When it comes to the status of HTMLBars and SVG support, I'd recommend following these folks on Twitter:

Kris Seldan
Erik Bryn
Matthew Beale
Stefan Penner
and of course Tom Dale

Generally, when there's a new release or a big, new feature added to Ember, Tom tweets it immediately. Stef's mostly good for information regarding Ember-CLI, but the others are all involved in the development of HTMLBars and SVG support, so they're important to follow. Also, when HTMLBars is released with SVG support, I'm liable to sing it from the rooftops. ;)

No comments:

Post a Comment

This form allows some basic HTML. It will only create links if you wrap the URL in an anchor tag (Sorry, it's the Blogger default)