This is a post about a pattern I find useful in my ember.js projects: I call them “loaders”. The idea is that you have an ember-concurrency task that loads data from ajax, or the store, or whatever async source you want, and a computed property that calls perform on that task. They usually look something like this:

// components/user-loader.js
import Component from '@ember/component';
import { task } from 'ember-concurrency';
import computed from 'ember-macro-helpers/computed';
import { inject } from '@ember/service';

export default Component.extend({
    store: inject(),

    loadData: task(function* (params) {
        return this.get('store').query('users', params);
    }),

    data: computed('foo', function(foo) {
        return this.get('loadData').perform({
            foo
        })
    })
});

{{! components/templates/user-loader.hbs}}
{{yield data data.isRunning}}

And you can use them like this:


{{#user-loader foo="bar" as |user isLoading|}}
    {{#if isLoading}}
        {{loading-spinner}}
    {{else}}
        {{user.value.name}}
    {{/if}}
{{/user-loader}}

There are several useful things about this:

  • You can put some model-specific logic in the loader component, so you don’t have to use it elsewhere
  • It takes loading of async data out of the model hook in your route, which means rendering isn’t blocked
  • It’s super easy to handle loading states, unlike the route-level loading hook which can get complicated quickly
  • Because it uses computed properties, it will automatically re-load the data every time a param changes.