Ember.js は、複雑なクライアント側アプリケーションを構築するための包括的なフレームワークです。その信条の1つは「設定より規約」であり、ほとんどのWebアプリケーションに共通する開発の大部分があり、したがってこれらの日常的な課題のほとんどを解決するための単一の最良の方法であるという信念です。ただし、適切な抽象化を見つけ、すべてのケースをカバーするには、時間とコミュニティ全体からの入力が必要です。推論が進むにつれ、解決策を見つける必要があるときに手を挙げて誰もが自分で身を守るのではなく、時間をかけてコア問題の解決策を正しく取得し、それをフレームワークに焼き付ける方がよいでしょう。
Ember.jsは常に進化しています 開発がさらに簡単 。ただし、他の高度なフレームワークと同様に、Ember開発者が陥る可能性のある落とし穴がまだあります。次の投稿で、これらを回避するためのマップを提供したいと思います。さっそく始めましょう!
アプリケーションに次のルートがあると仮定します。
Router.map(function() { this.route('band', { path: 'bands/:id' }, function() { this.route('songs'); }); });
band
ルートには動的セグメントid
があります。アプリに/bands/24
、24
などのURLが読み込まれる場合model
に渡されます対応するルートのフックband
。モデルフックには、セグメントを逆シリアル化して、テンプレートで使用できるオブジェクト(またはオブジェクトの配列)を作成する役割があります。
// app/routes/band.js export default Ember.Route.extend({ model: function(params) { return this.store.find('band', params.id); // params.id is '24' } });
ここまでは順調ですね。ただし、ブラウザのナビゲーションバーからアプリケーションをロードする以外に、ルートを入力する方法は他にもあります。そのうちの1つはlink-to
を使用していますテンプレートからのヘルパー。次のスニペットは、バンドのリストを調べて、それぞれのband
へのリンクを作成します。ルート:
{#each bands as } {{link-to band.name 'band' band}} {{/each}}
link-toの最後の引数band
は、ルートの動的セグメント、つまりそのid
を埋めるオブジェクトです。ルートのIDセグメントになります。多くの人が陥る罠は、モデルがすでにわかっていて渡されているため、その場合はモデルフックが呼び出されないことです。それは理にかなっており、サーバーへの要求を保存する可能性がありますが、確かにそうではありません。直感的。それを回避する独創的な方法は、オブジェクト自体ではなく、そのIDを渡すことです。
{band} {{link-to band.name 'band' band.id}} {{/each}}
ルーティング可能なコンポーネントがまもなくEmberに登場します。 おそらくバージョン2.1または2.2 。それらが着陸すると、動的セグメントのあるルートにどのように移行しても、モデルフックは常に呼び出されます。対応するRFCを読む ここに 。
Ember.jsのルートは、対応するテンプレートのコンテキストとして機能するコントローラーにプロパティを設定します。これらのコントローラーはシングルトンであるため、コントローラーがアクティブでなくなった場合でも、コントローラーで定義された状態は保持されます。
これは非常に見落としがちなものであり、 私もこれに出くわしました 。私の場合、バンドと曲を含む音楽カタログアプリがありました。 songCreationStarted
songs
のフラグコントローラは、ユーザーが特定のバンドの曲の作成を開始したことを示しました。問題は、ユーザーが別のバンドに切り替えた場合、songCreationStarted
の値がしつこく、半分完成した曲は他のバンドのためのもののようで、混乱していました。
勘定科目表の設計のベストプラクティス
解決策は、残したくないコントローラーのプロパティを手動でリセットすることです。これを行うための1つの可能な場所はsetupController
です。対応するルートのフック。afterModel
以降のすべての遷移で呼び出されます。フック(その名前が示すように、model
フックの後にあります):
// app/routes/band.js export default Ember.Route.extend({ setupController: function(controller, model) { this._super(controller, model); controller.set('songCreationStarted', false); } });
繰り返しますが、の夜明け ルーティング可能なコンポーネント この問題を解決し、コントローラーを完全に終わらせます。ルーティング可能なコンポーネントの利点の1つは、ライフサイクルがより一貫しており、ルートから移行するときに常に破棄されることです。彼らが到着すると、上記の問題は消えます。
setupController
でデフォルトの実装を呼び出さないEmberのルートには、アプリケーション固有の動作を定義するためのライフサイクルフックがいくつかあります。すでに見ましたmodel
これは、対応するテンプレートのデータをフェッチするために使用され、setupController
は、コントローラー、テンプレートのコンテキストを設定するために使用されます。
この後者のsetupController
には、model
からモデルを割り当てるという賢明なデフォルトがあります。 model
としてフックしますコントローラのプロパティ:
// ember-routing/lib/system/route.js setupController(controller, context, transition) { if (controller && (context !== undefined)) { set(controller, 'model', context); } }
(context
は、上記のember-routing
と呼ぶもののmodel
パッケージで使用される名前です)
モンテカルロシミュレーションの構築方法
setupController
フックは、コントローラーの状態をリセットするなど、いくつかの目的でオーバーライドできます(上記のよくある間違い2のように)。ただし、上記でEmber.Routeにコピーした親実装を呼び出すのを忘れた場合、コントローラーにmodel
がないため、長いヘッドスクラッチセッションに参加する可能性があります。プロパティセット。したがって、常にthis._super(controller, model)
を呼び出します。
export default Ember.Route.extend({ setupController: function(controller, model) { this._super(controller, model); // put the custom setup here } });
前に述べたように、コントローラー、およびそれらとともに、setupController
フックはすぐになくなるので、この落とし穴はもはや脅威ではありません。ただし、ここで学ぶべきより大きな教訓があります。それは、祖先での実装に注意することです。 init
Emberのすべてのオブジェクトの母であるEmber.Object
で定義されている関数は、注意が必要なもう1つの例です。
this.modelFor
の使用親以外のルートを使用するEmberルーターは、URLを処理するときに、各ルートセグメントのモデルを解決します。アプリケーションに次のルートがあると仮定します。
Router.map({ this.route('bands', function() { this.route('band', { path: ':id' }, function() { this.route('songs'); }); }); });
/bands/24/songs
のURLが与えられると、model
bands
、bands.band
のフックそしてbands.band.songs
この順序で呼び出されます。ルートAPIには特に便利なメソッドmodelFor
があり、そのモデルはその時点で確実に解決されているため、子ルートで使用して親ルートの1つからモデルをフェッチできます。
たとえば、次のコードは、bands.band
でバンドオブジェクトをフェッチするための有効な方法です。ルート:
// app/routes/bands/band.js export default Ember.Route.extend({ model: function(params) { var bands = this.modelFor('bands'); return bands.filterBy('id', params.id); } });
ただし、よくある間違いは、modelForでルート名を使用することです。 ない ルートの親。上記の例のルートがわずかに変更された場合:
Router.map({ this.route('bands'); this.route('band', { path: 'bands/:id' }, function() { this.route('songs'); }); });
bands
のように、URLで指定されたバンドをフェッチするメソッドは壊れます。 routeは親ではなくなったため、そのモデルは解決されていません。
// app/routes/bands/band.js export default Ember.Route.extend({ model: function(params) { var bands = this.modelFor('bands'); // `bands` is undefined return bands.filterBy('id', params.id); // => error! } });
解決策はmodelFor
を使用することです親ルートの場合のみ、modelFor
の場合は他の手段を使用して必要なデータを取得しますストアからのフェッチなどは使用できません。
// app/routes/bands/band.js export default Ember.Route.extend({ model: function(params) { return this.store.find('band', params.id); } });
ネストされたコンポーネントは、常にEmberで最も推論が難しい部分の1つです。の導入に伴い Ember1.10のブロックパラメータ 、この複雑さの多くは軽減されましたが、多くの状況では、子コンポーネントから起動されたアクションがトリガーされるコンポーネントを一目で確認するのは依然として困難です。
band-list
があるとしましょうband-list-items
を持つコンポーネントその中に、各バンドをリストのお気に入りとしてマークすることができます。
// app/templates/components/band-list.hbs {band} {{band-list-item band=band faveAction='setAsFavorite'}} {{/each}}
ユーザーがボタンをクリックしたときに呼び出されるアクション名がband-list-item
に渡されます。コンポーネントであり、そのfaveAction
の値になりますプロパティ。
band-list-item
のテンプレートとコンポーネントの定義を見てみましょう。
// app/templates/components/band-list-item.hbs {{band.name}} Fave this
// app/components/band-list-item.js export default Ember.Component.extend({ band: null, faveAction: '', actions: { faveBand: { this.sendAction('faveAction', this.get('band')); } } });
ユーザーが「これをお気に入りに追加」ボタンをクリックすると、faveBand
アクションがトリガーされ、コンポーネントのfaveAction
が起動されます渡されたもの(上記の場合はsetAsFavorite
)、 親コンポーネント上 、band-list
。
これは、ルート駆動型テンプレートからのアクションがコントローラー上で実行されるのと同じ方法でアクションが実行されることを期待しているため、多くの人をつまずかせます(そしてアクティブなルートでバブリングします)。これをさらに悪化させるのは、エラーメッセージがログに記録されないことです。親コンポーネントはエラーを飲み込むだけです。
原則として、アクションは現在のコンテキストで実行されます。非コンポーネントテンプレートの場合、そのコンテキストは現在のコントローラーですが、コンポーネントテンプレートの場合は、親コンポーネント(存在する場合)、またはコンポーネントがネストされていない場合は現在のコントローラーです。
したがって、上記の場合、band-list
コンポーネントは、band-list-item
から受け取ったアクションを再起動する必要があります。それをコントローラーまたはルートにバブルアップするため。
// app/components/band-list.js export default Ember.Component.extend({ bands: [], favoriteAction: 'setFavoriteBand', actions: { setAsFavorite: function(band) { this.sendAction('favoriteAction', band); } } });
band-list
の場合bands
で定義されましたテンプレート、次にsetFavoriteBand
アクションはbands
で処理する必要がありますコントローラまたはbands
ルート(またはその親ルートの1つ)。
ネストのレベルが多い場合(たとえば、fav-button
内にband-list-item
コンポーネントがある場合)、これはより複雑になることが想像できます。メッセージを出すには、内側からいくつかのレイヤーに穴を開け、各レベルで意味のある名前を定義する必要があります(setAsFavorite
、favoriteAction
、faveAction
など)。
s corp c corp llc
これは、 「改善されたアクションRFC」 、これはすでにマスターブランチで利用可能であり、おそらく1.13に含まれる予定です。
上記の例は、次のように簡略化されます。
// app/templates/components/band-list.hbs {#each bands as } {{band-list-item band=band setFavBand=(action 'setFavoriteBand')}} {{/each}}
// app/templates/components/band-list-item.hbs {{band.name}} Fave this
Emberの計算されたプロパティは他のプロパティに依存しており、この依存関係は開発者が明示的に定義する必要があります。 isAdmin
があるとしましょうロールの1つがadmin
である場合にのみ真である必要があるプロパティ。これは人がそれを書くかもしれない方法です:
isAdmin: function() { return this.get('roles').contains('admin'); }.property('roles')
上記の定義では、isAdmin
の値roles
の場合にのみ無効になります配列オブジェクト自体は変更されますが、アイテムが既存の配列に追加または削除された場合は変更されません。追加と削除も再計算をトリガーする必要があることを定義する特別な構文があります。
isAdmin: function() { return this.get('roles').contains('admin'); }.property('roles.[]')
Common Mistake No. 6の(現在修正されている)例を拡張して、アプリケーションでUserクラスを作成しましょう。
var User = Ember.Object.extend({ initRoles: function() { var roles = this.get('roles'); if (!roles) { this.set('roles', []); } }.on('init'), isAdmin: function() { return this.get('roles').contains('admin'); }.property('roles.[]') });
admin
を追加するとそのようなUser
の役割、私たちは驚きに満ちています:
var user = User.create(); user.get('isAdmin'); // => false user.get('roles').push('admin'); user.get('isAdmin'); // => false ?
問題は、ストックJavascriptメソッドが使用されている場合、オブザーバーが起動しない(したがって、計算されたプロパティが更新されない)ことです。これは、 Object.observe
ブラウザでは改善されますが、それまでは、Emberが提供する一連のメソッドを使用する必要があります。現在の場合、pushObject
push
と同等のオブザーバーフレンドリーです。
user.get('roles').pushObject('admin'); user.get('isAdmin'); // => true, finally!
star-rating
があると想像してくださいアイテムの評価を表示し、アイテムの評価を設定できるコンポーネント。評価は、歌、本、またはサッカー選手のドリブルスキルに対して行うことができます。
テンプレートでは次のように使用します。
{song} {{star-rating item=song rating=song.rating}} {{/each}}
さらに、コンポーネントが星を表示し、各ポイントに1つの完全な星が表示され、その後、最大評価まで空の星が表示されると仮定します。星がクリックされると、set
アクションはコントローラーで実行され、ユーザーが評価を更新したいと解釈する必要があります。これを実現するために、次のコードを書くことができます。
// app/components/star-rating.js export default Ember.Component.extend({ item: null, rating: 0, (...) actions: { set: function(newRating) { var item = this.get('item'); item.set('rating', newRating); return item.save(); } } });
それで仕事は終わりますが、いくつか問題があります。まず、渡されたアイテムにrating
があることを前提としています。プロパティであるため、このコンポーネントを使用してLeo Messiのドリブルスキルを管理することはできません(このプロパティはscore
と呼ばれる場合があります)。
次に、コンポーネント内のアイテムの評価を変更します。これは、特定のプロパティが変更される理由を理解するのが難しいシナリオにつながります。同じテンプレートに別のコンポーネントがあり、その評価も使用されているとします。たとえば、サッカー選手の平均スコアを計算するために使用されます。
このシナリオの複雑さを軽減するためのスローガンは、「データダウン、アクションアップ」(DDAU)です。データは(ルートからコントローラー、コンポーネントに)渡される必要がありますが、コンポーネントはアクションを使用して、これらのデータの変更についてコンテキストに通知する必要があります。では、ここでDDAUをどのように適用する必要がありますか?
評価を更新するために送信する必要のあるアクション名を追加しましょう。
{song} {{star-rating item=song rating=song.rating setAction='updateRating'}} {{/each}}
次に、その名前を使用してアクションを送信します。
コンサルティングビジネスのためのクライアントを取得する方法
// app/components/star-rating.js export default Ember.Component.extend({ item: null, rating: 0, (...) actions: { set: function(newRating) { var item = this.get('item'); this.sendAction('setAction', { item: this.get('item'), rating: newRating }); } } });
最後に、アクションはコントローラーまたはルートによってアップストリームで処理されます。ここで、アイテムの評価が更新されます。
// app/routes/player.js export default Ember.Route.extend({ actions: { updateRating: function(params) { var skill = params.item, rating = params.rating; skill.set('score', rating); return skill.save(); } } });
これが発生すると、この変更はstar-rating
に渡されたバインディングを介して下方に伝播されます。その結果、表示される星の数が変化します。
このように、コンポーネントでミューテーションが発生することはなく、アプリ固有の部分はルート内のアクションの処理のみであるため、コンポーネントの再利用性が損なわれることはありません。
サッカーのスキルにも同じコンポーネントを使用できます。
{skill} {{star-rating item=skill rating=skill.score setAction='updateSkill'}} {{/each}}
ここで書いたものを含め、人々が犯した(または自分自身を犯した)間違いのいくつか(ほとんど?)は、2.xシリーズの早い段階で消えるか大幅に軽減されることに注意することが重要です。 Ember.jsの。
残っていることは上記の私の提案で対処されているので、Ember 2.xで開発すると、それ以上エラーを起こす言い訳はできなくなります。この記事をPDFとしてご希望の場合は、 私のブログに行きなさい 投稿の下部にあるリンクをクリックします。
私はフロントエンドの世界に来ました Ember.js 2年前、私はここに滞在しています。私はEmberに非常に熱心になり、ゲストの投稿と自分のブログの両方で熱心にブログを書き始め、会議で発表しました。私も本を書きました、 Ember.jsでロックンロール 、Emberを学びたい人のために。サンプルチャプターをダウンロードできます ここに 。