Rendering a Promise Before it Resolves

At this point, you already asked yourself if your component should load data.

If so, how do you render UI while waiting for a promise to resolve? Should you place a conditional in the template? Fetching data from the server inside the component is good practice?

Let’s have a look.

Ember Igniter has now a full-featured article about rendering promise-backed computed properties:

The Guide to Promises in Computed Properties

Data-thirsty Dropdown Menu

In the app’s navigation bar, we have a component that suggests cities you may like. It doesn’t make sense to load this in a route, as this concern has a longer life than (and is independent of) the route.

It isn’t architecturally sound for the component to make an ajax call. That’s what we’ll use a Service for:

// app/services/geo.js

export default Ember.Service.extend({

  cities() {
    // this simulates fetching data across the network
    return new Ember.RSVP.Promise(function(resolve) {
      Ember.run.later(function() {
        resolve(["Paris", "Washington DC", "Bogota", "Nairobi", "Auckland"]);
      }, 3000);
    });
  }

});

We’ll inject this service in our bare-bones drop-down component:

// app/components/drop-down.js

export default Ember.Component.extend({

  geo: Ember.inject.service(),

  cities: null,

  didInsertElement() {
    this._super(...arguments);
    this.get('geo').cities().then((cities) => {
      this.set('cities', cities);
    });
  }

});

If you were to load from Ember Data, inject the store Service. Remember all store calls already return promises!

// app/components/drop-down.js

export default Ember.Component.extend({

  store: Ember.inject.service(),

  cities: null,

  didInsertElement() {
    this._super(...arguments);
    this.get('store').findAll('city').then((cities) => {
       this.set('cities', cities);
    });
  }

});

And

{{! app/templates/components/drop-down.hbs }}

{{#if cities}}
  <select>
    {{#each cities as |city|}}
      <option>{{ city }}</option>
    {{/each}}
  </select>
{{ else }}
  Loading...
{{/if}}

That’s right: the component will call the cities method of the Service, which returns a promise.

It will do that on didInsertElement (i.e. when the component is available in the DOM) or upon any other component lifecycle hook. When the promise resolves, it will populate the cities property – and the template reacts to this accordingly.

Sure, we haven’t handled errors and there are variations of the problem. But this should give you this gist of how to approach it!

Much more in the guide to Promises in Computed Properties.

Enjoyed this article? Don't miss my next one!

Leave me your e-mail for content that will help you master Ember:

Do you want to master Ember fast?

Leave me your e-mail for helpful updates delivered straight to your inbox.

(A few e-mails per month. No BS. Unsubscribe anytime!)