テーマで Ember.PromiseProxyMixin を使用する

最近、Discourse コア内の Ember モジュールに Ember.PromiseProxyMixin が追加されました。

これは非常に便利な Ember ミックスインで、非同期リクエストを実行し、Ember コンポーネント内で Promise を簡単に扱うことを可能にします。Ember 版の React Suspense と考えてください。Discourse におけるテーマやプラグイン開発の可能性は非常に大きいです。

以下は、Ember の公式ドキュメント(Ember.PromiseProxyMixin について)です。

また、Ember.PromiseProxyMixin の使い方を詳しく解説した、Discourse 以外の例もご覧ください。

ここでは、Discourse テーマ内でこのミックスインをどのように使えるか、簡単な例を示します。例えば、何らかの理由で topic-list-item コンポーネント内で、投稿者がスタッフメンバーの場合にアバターをスタイリングしたいとします。その場合、アバターには異なるボーダーを付ける必要があります。

topic-list-item.js(.es6) のどこかで以下の処理を行う必要があります:

  • EmberObjectPromiseProxyMixin をインポートし、EmberObjectPromiseProxyMixin で拡張します。
import EmberObject from "@ember/object";
import PromiseProxyMixin from "@ember/object/promise-proxy-mixin";

const PromiseObject = EmberObject.extend(PromiseProxyMixin);

// 同じユーザーへの重複リクエストを防ぐために、カスタム memoize 関数を使用できます
const getUser = memoize(username => ajax(`/u/${username}`).then(data => data));
  • 次に、以下の計算プロパティを作成します。
  // 計算された Promise
  @discourseComputed("topic")
  posterPromise(topic) {
    const { user } = topic.posters[0];
    return getUser(user.username);
  },

  // この計算プロパティは Promise を `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;
    // ユーザーがスタッフメンバーかどうかを確認するには、
    // スタッフグループに所属しているかチェックする必要があります
    return groups.some(({ name }) => name === "staff");
  }

そして、テンプレート topic-list-item.hb(r|s) には以下のようなコードが書かれます:

  {{#if posterProxy.isFulfilled}}
    {{#if isStaff}}
      // カスタマイズ処理を実行
    {{/if}}
  {{/if}}

isPending を活用して、ローダーを表示することも可能です。

このあまり役に立たない例かもしれませんが、ミックスインの仕組みを理解する助けになれば幸いです。もしかすると、テーマやプラグイン開発でどのように活用できるかを見つけるきっかけになるかもしれません。

:wave:

「いいね!」 4

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