Using Ember.PromiseProxyMixin in discourse development

Recently the Ember.PromiseProxyMixin was added to ember modules in the discourse core.

It’s very useful Ember mixin which allows you to make async requests and to work easily with Promises inside Ember components. Think of it as React Suspense but for Ember. The potential is huge for theme and plugin development in discourse.

Here is the official Ember documentation for Ember.PromiseProxyMixin.

And here you can find a detailed non-discourse example how to use Ember.PromiseProxyMixin.

I’ll give a simple example how can this mixin be used inside discourse theme. Let’s say for some reason in topic-list-item component you want to style the poster avatar if the poster is staff member, the avatar has to have a different border.

Somewhere in topic-list-item.js(.es6) you will have to:

  • Import EmberObject and PromiseProxyMixin and extend EmberObject with PromiseProxyMixin
import EmberObject from "@ember/object";
import PromiseProxyMixin from "@ember/object/promise-proxy-mixin";

const PromiseObject = EmberObject.extend(PromiseProxyMixin);

// You can use a custom memoize function to prevent multiple request for same users
const getUser = memoize(username => ajax(`/u/${username}`).then(data => data));
  • Then you will have make these computed properties
  // Computed Promise
  @discourseComputed("topic")
  posterPromise(topic) {
    const { user } = topic.posters[0];
    return getUser(user.username);
  },

 // This CP wraps the promise with with `PromiseObject`
  @discourseComputed("posterPromise")
  posterProxy() {
    const promise = this.get("posterPromise");
    return promise && PromiseObject.create({ promise });
  },

  posterData: reads("posterProxy.content.user"),

  @discourseComputed("posterData")
  isStaff() {
    const posterData = this.get("posterData");
    if (!posterData) return false;
    const { groups = [] } = posterData;
    // To see if the user is a staff member we have to check
    // if the use belongs to a staff group
    return groups.some(({ name }) => name === "staff");
  }

And in the template topic-list-item.hb(r|s) you would have something like this:

  {{#if posterProxy.isFulfilled}}
    {{#if isStaff}}
      // do your customisation
    {{/if}}
  {{/if}}

You can utilise isPending to show a loader maybe.

I hope this not-so-useful example can help you figure out how the mixin works and maybe it helps you find a way you could use it in your theme/plugin development.

:wave:

4 Likes

Hi @zcuric,

thanks for this very nice write up and the initial PR, there’s definitely things to explore here, in plugins/themes but also in the Discourse codebase. We will see how to apply this pattern and maybe make it simpler in the future :+1:

5 Likes