Data Down Action... Down in Emberjs - Using Ember.Evented
On the other hand, if you are unfamiliar with this concept, you’re probably wondering what these four words even mean.
Data down action up (DDAU) is a best-practice concept in Ember. It assumes that your data flows from the up (the owners of the data, as routes) to the down (presenters of that data, like components). On the other hand, actions to change data flow from the receivers of these actions, from the user (like components’ buttons) up to the owners whose job it is to perform them (like routes). This architecture should help you maintain the code and the application. I’m a big fan and supporter of DDAU, but I have also come across edge cases where it doesn’t apply.
So why might you design data flow differently? Basically, sometimes the actions are not triggered by users directly on the components. Let’s say that we have a to-do list that can be controlled via the keyboard. The user can move up or down through the list and extend descriptions by clicking Enter. We want it to be controlled independently of the DOM element that currently has focus. So we use some addon that magically intercepts keyboard events on the controller level (exactly how doesn’t matter). To see the big picture, take a look at this Ember Twiddle, which has emulated UP/DOWN/ENTER keys at the bottom. So how can we trigger an action on the currently active component when receiving it directly to the controller?
This is where we can take advantage of data down, action… down.
Composable components
Everytime I am write or talk about components and data flow, I can’t resist mentioning an exceptionally good presentation by Miguel Camba called Composable Components. To give a short summary of the idea - Miguel managed to provide an excellent way to revert the actions flow by yielding the public API of the component. This lets you make use of the public API by handling it e.g. in another component rendered within the yielding one:
Unfortunately, this has limited usage. It can’t be used in our use case. Even though it’s brilliant, all it makes use of is JavaScript closures, when the real action performer is a DOM element inside the yielding component. We want to handle the event without building any closure that will point out the eventual receiver.
Ember.Evented
What is Ember.Evented? Have you ever heard of it? It’s a mixin that can be used in any Ember Object you want. It provides a very simple, yet powerful, API to trigger and receive events across the browser document.
What is the best use case for Ember.Evented? Create an Ember Service that mixes in Ember.Evented. Then, we can use it as a proxy between any objects with which we want to be able to communicate. In our example, we will inject the service into a controller to trigger events on it when key presses happen. On the other hand, we will listen to these events in every component that should perform some action on these events. This simple pattern lets you communicate between objects that cannot easily communicate due to the parent-child relationships. Here you can check out the full code of the service, component and controller. Below you can see a short snippet:
Its underlying logic is quite obvious. Basically, what we do when running on
is to add the method to the meta-object that stores all the listeners. When triggering the event, it traverses the array of listeners and executes each function in reverse order, directly from the meta-object. Simple, yet powerful.
Summary
Data Down Action Down is not a pattern we should be using often, as it introduces consequences to our application that can be difficult to reason about. It’s also very easy to introduce memory leaks when we forget to remove listeners by running off
on object destroy. Ember.Evented is rather a solution for edge cases. It’s useful to be aware of such possibilities and understand how they work.
Have you used Ember.Evented in your work? Have you bumped into some problems with this pattern? Drop a comment and share your experience!
If you are interested in the hidden opportunities and pitfalls of Ember.js, check out my extensive ebook about the Ember Run Loop.