Indicating Progress - Loading Routes in Ember.js
18 June 2014
When an Ember app is being loaded at first, the user sees a blank screen. When transitioning between two routes and loading data to render for the destination template, there is no indication that something is happening and thus the user can be perplexed. Ideally, loading data happens fast enough so that users barely notice the blank screen or freeze, but web apps do not always behave ideally.
If you have read some of my other articles on Ember, you will not be surprised to hear that there is a splendid solution for this problem and that it is based on a convention.
model hook of routes is the canonical place to fetch data needed to render the corresponding template. When a promise is returned, execution is blocked until that promise is resolved (or rejected). That is the period during which the application seems unresponsive. When starting up the application, this can even mean that the user only sees a blank screen. Since data is fetched asyncronously, not even the spinner in the tab title is going to spin.
The user may just close the tab and go on with her life. We can't let that happen and unsurprisingly Ember has a convention-based solution that is easy to work with and customize.
Beautiful, reusable conventions
We saw how each resource route (and thus each route level) creates an outlet for the level below to render content in. Let's take a look at the routes of the application to see how that plays out:
The outlet defined in the (top-level) application template is going to be filled in by the content rendered by the first-level templates (in this case,
Next, the outlet in the
artists template is populated by the routes below the artists resource route:
The artist template also defines an outlet into which content is rendered by the routes below the artist resource. In this case, there is no common markup for a single artist so the template only defines the outlet:
Finally, there is a single route defined below the
Loading routes build on the nested routes pattern
The ingenious thing about indicating that loading data from the backend is happening is that it leverages the "nested routes, nested templates" architecture explained above.
When the model hook of a certain route returns a promise that is not resolved, a so-called loading template at the same level as the route is going to be rendered.
When loading the list of artists in the
artists route, a loading route at the same level is going to be activated. That, by default, means a top-level loading template is rendered.
Consequently, all we need to do is to put into that template what we want the user to see while data is being fetched. Currently that is what she sees in that scenario:
You think we can beat that?
Top-level loading template
The convention says that the loading route (and thus template) should be the sibling of the route we define it for and should be called
The loading template for the
artists route should consequently be called
Let's inspect what our app looks like while loading data from the backend:
Nice, but it gets nicer still.
Loading route for the songs
We can now descend a couple of levels and make a loading template for when the songs of a certain artist are being fetched.
Let's take a look at the "routing table" to see what that template should be named:
The loading route should be the sibling of the route under consideration.
In this particular case, this latter is
artist.songs and thus the loading template should be named
Customizing loading route behavior
More precisely, what happens when entering a route where one of the model hooks returns an unresolved promise is that a
loading event is fired on the route.
We saw above that the default implementation is to look up a route at the same level as the route itself and render the corresponding template.
However, this behavior is customizable. For example, we can pop up an alert box in 1993-style, telling the user she needs to wait.
Below, I do that for the top-level
Another thing we can do is to render another loading template:
Which also just works:
Since handling the loading state happens in the route, the most powerful piece of Ember architecture, the possibilities are many.
Why I love this solution and why you should, too
In summary, let's see what Ember's solution of indicating the loading of data brings to the table:
- It uses a convention to eliminate boilerplate and spare you from having to come up with your own solution.
- It allows different markup and text for the different "slow" parts. By default, each loading template will be rendered exactly where the data would be rendered.
- If this default behavior does not suit your taste or needs, there are several ways to tweak it, in well-defined ways.
Note: The latest version of the code that contains these changes is available on Github.Share on Twitter