Warning: This was created with older versions of Ember.js and is likely no longer relevant. Please tread lightly when referencing this article.
SVG is a vector graphic that scales well and is great for vibrant user interfaces. This post doesn’t go into SVG per se, but there is a great article on SVG and it’s application in Web Development over at CSS Tricks.
SVG and Ember make for an exciting pair. Binding values and responding to events with SVG and Ember components just works and can create beautiful, fast, and responsive interfaces, easily. There was just a presentation on the matter at EmberConf 2017 by @jwwweber that really showcases the flexibility of SVG with Ember.
In my scenario, I had a model, let’s call it
Location, which had a
belongsTo('image') for the
Image model which contained a
url attribute. This
url was the asset url for the
Location’s icon. This asset url could be a
svg in certain cases but I knew the data coming into my component was only
Location models with
An initial option may be to use an
<object> tag with the external
svg url in the component template and maybe even a fallback
<img> tag inside it. This technique for rendering SVGs is covered in more detail in the CSS Tricks article for those unfamiliar.
For this approach the component may look something like this.
Ok, so this works but it isn’t the best for a handful of reasons. You can check out the implementation in action at Ember Twiddle: Part 1.
First, there are 70 external SVGs in this example being requested and then rendering and its slow. Noticeably slow.
The other downside to doing things this way is that the SVG document is contained within the
<object> tag, which means it isn’t accessible for manipulation, data binding, or styling. The SVG elements I was working only contained a
path and had no fill, which made them difficult to click by default. I had to add
pointer-events: all to the
<svg> element itself but with this SVG living inside it’s own document inside the
<object> this was a problem.
If I could get the contents of the document via an Ajax request and then load the contents in the template directly, I would then have inline SVG which is what I ultimately wanted. This would allow me to manipulate the contained svg as desired.
So I iterated towards using an
ember-concurrency task to make the request for the SVG document body. Ember-concurrency allows for better control of async requests along with managing state and child tasks. Since learning about it I’ve used it quite frequently to manage my async tasks.
Putting the SVG document request task within the component posed the same issue, the request was being made 70 times. I wanted it loaded once for the
Image model as many of the
Location models were sharing the same
Image model. I considered finding a way to add it to the
ImageAdapter but digging around in the [Ember Data][ember-data] adapter code didn’t reveal any elegant way to chain two ajax requests together and coalesce a final response to be passed on to the serializer and pushed into the store.
Instead, I decided to move the request logic into the
Image model itself. This required I add an extra check to ensure the
Image url was indeed an
svg. Now only one request would be made per
Image and the speed gain on render was enormous and obvious. I also now had inline
<svg> which meant I could style to my hearts content.
You can check out this final implementation in action at Ember Twiddle: Part 2.
I am very interested in hearing about other solutions to this problem, especially if they hook into the model’s Ember Data Adapter at all. I feel that requests for data should really belong there, but this achieved the result I was looking for. Please comment away!