Promise-aware controllers and components with Ember.js

Posted by Kevin Soltysiak on June 2, 2015 Topics: ember.js

When building a web application, whether server- or client-rendered, it is pretty common to display a spinner for UI sections that might take some time to load.

Ember.js makes it pretty easy for us to achieve this: if you are fetching your data in your routes (as your should be) and returning promises from the model hook, an intermediate route (named loading) will be entered (and its template rendered) while the promise is resolved:

See example on emberjs.jsbin.com

This substate is also available for nested routes: when AppPostCommentsIndexRoute is fetching its model, AppPostCommentsLoadingRoute will be entered and the template post/comments/loading will be rendered.

While very useful, there's one frequent use-case not covered with those substates. What if there is several components in your interface, each backed by data coming from a different source? You will need to handle the spinners independently, and that is not something you can do with the route substates.

To the rescue, Ember.PromiseProxyMixin! This mixin makes any subclasse of ObjectProxy, ObjectController or ArrayControllers promise-aware, according to the docs. When you include this mixin in one of your controllers, all you need to do is set a promise property on it. Then, you can use various properties such as isSettled, isFulfilled, isRejected, isPending... If the promise succeeds, the content property will be set. And if the promise rejects, the reason property will be filled with the error.

Best part? It works with components too! Though the docs does not mention them, I have yet to encounter an issue when using this mixin in my components. See for yourself:

See example on emberjs.jsbin.com

As you can see, pretty straightforward. Make sure you do not return a promise from your route, otherwise the loading substate will be entered. One downside is that you cannot choose the name of the properties: you have to stick with promise and content, and the latter is not the most semantic of names. You can always make an alias of this is really an issue, though.

If you are using this mixin with controllers, one issue you will probably encounter is that wether your component is top-level (and linked to the route) or instantiated using or with itemController, Ember will by default fill the content property. In those cases, you will need to override init in your controller, set promise to the value of content and clear content.

I hope this post was useful. If you have anything to add, please leave a comment or hit me up on twitter (@ksol) !