Rendering resolved promise value in Ember handlebars template


Is there a good way to render the result of a promise in a handlebars template?

For example, I have the following model:

App.TopicItem = DS.Model.extend({
  topic: DS.belongsTo('topic'),
  paddedPosition: function() {
    return this.get('topic.course.lessons').
      then(function(lessons) {
        return lessons.indexOf(topicItem);
      }).
      then(function(index){
        var position  = index;

        if (position < 0) { 
          return;
        }

        position = position + 1;

        return (position < 10 ? $.rjust(position, 2, '0') : position.toString());
      });
  }.property('topic.course.lessons')
});

And I would like to render the value of the position in the handlebars template like this:

{{topicItem.paddedPosition}}

Is there a good way to accomplish this?

You could have the property lazily set itself, something like:

App.TopicItem = DS.Model.extend({
  topic: DS.belongsTo('topic'),
  paddedPosition: function(key, value) {
    if (arguments.length > 1) {
      // > 1 args = this is a `set`
      return value;
    } else {
      // otherwise this is a `get`
      var _this = this;
      value = null;

      this.get('topic.course.lessons').
        then(function(lessons) {
          // do stuff based on result
          var padded = ...;
          // when the promise is resolved, set this property with the value
          _this.set("paddedPosition", padded);

          // if the promise resolves immediately, set `value` so we return
          // the calculated value and not null
          value = padded;
        });

      // returns null if the promise doesn't resolve immediately, or 
      // the calculated value if it's ready
      return value;
    }
  }.property('topic.course.lessons')
});

When it's first accessed it'll kick-off the calculation, likewise any time the lessons change, then it'll set itself as the result of the calculation once it's done.

This works because a computed property is called both on get and set, you can differentiate between the two by the number of arguments - 1 for get, more than 1 for set (it used to be 2, now you get 3 so the best way to detect is > 1). More on that in the docs.

Whatever's returned from a computed property (either in get or set) is cached until its dependent properties change - in this case topic.course.lessons.

In the above example, when the first get comes in we kick off the calculation and return null. This is now cached as the value of the property, if anything else calls this property before the promise has resolved then it'll return null.

Once the promise resolves, we call set on the same property with the calculated value. This we just return in the setter and it's now cached as the value of the property.

Until the dependent properties change (topic.course.lessons), or a new value is set then the cached value is returned from the property.


That seems like an unexpected behaviour. There's a bug filled for that problem: https://github.com/emberjs/ember.js/issues/11046