読者です 読者をやめる 読者になる 読者になる

Ember 2.10 リリース

Ember.js

Release v2.10.0 · emberjs/ember.js · GitHub

まだブログ記事は公開されていませんが、草稿が以下から読めます。

website/2016-11-28-ember-2-10-released.md at ember-2-10-release · emberjs/website · GitHub

このリリースでは Glimmer 2 と呼ばれる新しいレンダリングエンジンがデフォルトで有効になりました。完全な後方互換性を保ちつつもパフォーマンスが向上しているだけでなく、将来的に angle bracket components などの新機能を導入するために必要な拡張性を備えているとのことです。

ヘルパーが再描画されるタイミング

Ember.js

ユーザの名前を表示するヘルパーを作るとします。こんな感じになるでしょうか。

// app/helpers/user-name.js

import { helper } from 'ember-helper';

export default helper(([user]) => {
  return `${user.get('firstName')} ${user.get('lastName')}`;
});
{{user-name user}}

一見よさそうですが落とし穴があります。ヘルパーは引数の値自体が変化したときは再描画されますが、その先のプロパティは見てくれません。この例だと user.firstNameuser.lastName が変化しても表示内容はそのままになってしまいます。

一番簡単な対処方法は、ヘルパーの引数には常に単純な値を渡すようにすることです。

// app/helpers/user-name.js

import { helper } from 'ember-helper';

export default helper(([firstName, lastName]) => {
  return `${firstName} ${lastName}`;
});
{{user-name user.firstName user.lastName}}

それでは引数が多くなりすぎて困るようなら、クラスベースのヘルパーにするという手があります。

// app/helpers/user-name.js

import Helper from 'ember-helper';
import observer from 'ember-metal/observer';

export default Helper.extend({
  compute([user]) {
    this.set('user', user);

    return `${user.get('firstName')} ${user.get('lastName')}`;  
  },

  userDidChange: observer('user.{firstName,lastName}', function() {
    this.recompute();
  })
});
{{user-name user}}

表示を更新したいタイミングで this.recompute() を呼び出してやればよいわけです。

クラスベースのヘルパーはサービスを inject するような使い方もできます。詳しくは公式ガイドの Class-based Helpers を参照してください。

Ember.Sapporo Meetup #29

Ember.js Ember.Sapporo

Ember.Sapporo Meetup #29

ThoughtWorks Technology Radar

Technology Trends for 2016 | Technology Radar | ThoughtWorks

Languages & Frameworks で Ember.js が adopt に。すごい。

Bower の話

npm から yarn に移行したけれど、bower はどうしたらいいんですかねえという話。

yarn も不完全ながら bower に対応しているので、ちゃんとできるまで待っていれば…と思ったら bower サポートはなくなった模様。

Remove bower support by kittens · Pull Request #1441 · yarnpkg/yarn · GitHub

ember-cliapp.import が node_modules に対して使えない理由のひとつにパフォーマンスの問題があるらしい。bower を捨てるときが来たら import from 'npm:package-name' をサポートするとのこと(つまり browserify を組み込みでサポートするということ)。

デフォルトで依存するライブラリは徐々に bower から npm に移行しつつある。過去には ember-data や jquery が npm 管理下に移っている。

Ember 自体のソースも npm パッケージにする動きがある。

Moves ember dependency to `ember-source` npm package by locks · Pull Request #6324 · ember-cli/ember-cli · GitHub

JavaScript Modules API

RFC: JavaScript Modules API by tomdale · Pull Request #176 · emberjs/rfcs · GitHub

細かい粒度で import できるようにして、使うコードだけビルド結果に含められるようにしようという提案。

途中に出てくる Scoped Package (@ember/object のような)はなんの意味を持つのだろうと思ったら、npm にそういう仕組みがあるらしい。

scope | npm Documentation

FastBoot を試してみた話

Ember FastBoot

Ember アプリケーションのサーバサイドレンダリング。ブラウザ固有の機能(geolocation とか)は当然動かないので、fastboot service を inject して適宜分岐するといいよ、云々。

プロダクション環境では fastboot-app-server を使うとよさそう。

GitHub - ember-fastboot/fastboot-app-server: A production-ready app server for running Ember FastBoot apps

Ember Data で hasMany な関連をフェッチせずにカウントする

Ember.js Ember Data

ブログアプリで post has many comments な関連があるとします。記事の一覧にそれぞれコメントの件数を出すにはどうしたらよいでしょうか。

<ul>
  {{#each posts as |post|}}
    <li>
      {{link-to post.title 'post' post}}
      (コメント: {{post.comments.length}}件)
    </li>
  {{/each}}
</ul>

これで意図通り動きはするのですが、post.comments.length が評価されたタイミングですべてのコメントがフェッチされてしまいます。動作が遅くなってしまうので、記事を開くまでコメントの実体はロードしたくありません。

このようなケースを扱うため、Ember Data 2.5 で References(参照)という API が導入されました。
Ember.js - Ember Data 2.5 and 2.6 Beta Released

DS.Model#hasMany で関連の参照を取得できます(関連を定義する DS.hasMany とは別物です)。ここから id の配列を取り出せますので、これをカウントしてやれば件数がわかります。

// app/models/post.js

import Model from 'ember-data/model';
import { hasMany } from 'ember-data/relationships';
import computed from 'ember-computed';

export default Model.extend({
  comments: hasMany('comment'),

  numComments: computed('comments.length', function() {
    const commentsRef = this.hasMany('comments');

    return commentsRef.ids().length;
  })
});
<ul>
  {{#each posts as |post|}}
    <li>
      {{link-to post.title 'post' post}}
      (コメント: {{post.numComments}}件)
    </li>
  {{/each}}
</ul>

この機能は emberjs/rfcs#57 で提案され、emberjs/data#3303 で実装されました。当時のやり取りに興味があれば眺めてみると面白いでしょう。