Dynamic component layout in Ember
Working with Ember is fun. You play around with components, experiment your way through development, and things go quite smoothly — well, mostly in the beginning. In my experience with the framework, the nightmares usually start when the project and the complexity of your use cases start to grow. I encountered one such scenario recently that required my component layout to be changed dynamically along with the data.
Now what you just read on the last line might sound crazy. The protectors of the Ember realm would probably say that it falls in the uncharted territories and believe me, the hunt for a solution to this problem, as simple as it may sound, took me places on the internet.
So let’s move to the problem statement and a small hack to solve it. I suggest reading the problem stated carefully to fully appreciate the solution.
Thank me later ;)
Problem
Dynamically change the component layout w.r.t the incoming async data.
That is the simplest way I can put it. But for all we know, that is not the way component layouts work in Ember right? Layouts in Ember components are static and rendered only once while it is the data that is dynamic and two-way bound which keeps updating. For example, you cannot do this with the layout property of a component:
layout: Ember.computed(‘some_property’, function() {
// code that returns a template to render
})
The immediate idea that pops up in one’s mind is to use multiple components which can be switched inside a parent component on a case basis. And that is exactly what my initial approach was. But then a friend and colleague Mayank dixit said this:
What if you have got not 3–4 but 4000 dynamically changing templates?
To further complicate the problem, let’s modify it a little-
Dynamically change the component layout w.r.t the incoming async data and template.
Which means that the templates instead of being stored locally, will be fetched asynchronously.
The switching components approach is not so much of a solution now is it?
Solution
Enter Ember Run loop which is at the heart of Ember’s functionalities and also a lot of errors one might face while doing things with Ember one’s not usually supposed to do. You might have come across something like this:
You modified *** twice in a single render. This was unreliable in Ember 1.x and will be removed in Ember 2.0
That’s the ember run loop at play. (Read more about Ember’s Run loop)
The solution to our problem lies with Ember run loop. In fact the Ember guidelines themselves describe its usage as follows:
The most common case for using the run loop is integrating with a non-Ember API that includes some sort of asynchronous callback. For example:
* DOM update and event callbacks
* setTimeout and setInterval callbacks
* postMessage and messageChannel event handlers
* AJAX callbacks
* Websocket callbacks
Although we cannot make the component’s layout property computed, we can place an observer on the changing templates. But this can prove fatal for the run loop if not handled carefully. The scheduleOnce method of the Ember’s run api can allow us to execute a function once the current rendering has finished.
This approach uses two components to achieve the desired goal.
One component is parent and has all the implementation ranging from dynamically compiling the string templates and assigning it to the layout of the dynamic child component.
The other component is merely a place holder (but capable of performing other important operations if needed) to support dynamically changing layouts passed to it through the parent component’s template.
NOTES:
* The templates are raw strings compiled using Ember HTMLBars which needs the ember-template-compiler dependency to be imported in your ember-cli-build.js
* The templates can use your ember helpers like they would normally do inside an hbs file
Following is the gist to the solution using two minimal templates to demonstrate the approach.
Or you can go experiment with it right-away on this Ember twiddle.
Well, that is all about it. Thanks for reading and do show your support via claps if you liked this. If you liked it a lot, go ahead and hit those follow buttons :)
More coming soon..