ドキュメントを読むと、次のようにモデルをルートに割り当てる必要がある(または割り当てる必要がある)ように見えます。
App.PostRoute = Ember.Route.extend({
model: function() {
return App.Post.find();
}
});
特定のルートで複数のオブジェクトを使用する必要がある場合はどうなりますか?つまり、投稿、コメント、ユーザーですか?それらをロードするルートを指示するにはどうすればよいですか?
最後の更新は永遠に:これを更新し続けることはできません。したがって、これは非推奨であり、おそらくこのようになります。ここに、より良い、より最新のスレッドがあります EmberJS:複数のモデルを同じルートにロードする方法?
更新:私の元の答えでは、モデル定義でembedded: true
を使用するように言った。それは間違っています。リビジョン12では、Ember-Dataは、外部キーが接尾辞( link )で定義されることを想定しています。単一レコードの場合は_id
、コレクションの場合は_ids
です。次のようなもの:
{
id: 1,
title: 'string',
body: 'string string string string...',
author_id: 1,
comment_ids: [1, 2, 3, 6],
tag_ids: [3,4]
}
私はフィドルを更新しました。何か変更があった場合、またはこの回答で提供されたコードでさらに問題が見つかった場合は、再度更新します。
関連モデルの回答:
あなたが説明しているシナリオでは、モデル間で 関連付け に依存します ( embedded: true
を設定)Post
モデルにDS.hasMany
関連付けを定義し、両方のComment
にDS.belongsTo
関連付けを定義できることを考慮して、そのルートにUser
モデルのみを読み込みますComment
およびPost
モデル。このようなもの:
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
email: DS.attr('string'),
posts: DS.hasMany('App.Post'),
comments: DS.hasMany('App.Comment')
});
App.Post = DS.Model.extend({
title: DS.attr('string'),
body: DS.attr('string'),
author: DS.belongsTo('App.User'),
comments: DS.hasMany('App.Comment')
});
App.Comment = DS.Model.extend({
body: DS.attr('string'),
post: DS.belongsTo('App.Post'),
author: DS.belongsTo('App.User')
});
この定義により、次のようなものが生成されます。
この定義を使用すると、投稿をfind
するたびに、その投稿に関連付けられたコメントのコレクション、コメントの作成者、投稿の作成者であるユーザーにアクセスできます。 すべて埋め込まれているため。ルートはシンプルなままです:
App.PostsPostRoute = Em.Route.extend({
model: function(params) {
return App.Post.find(params.post_id);
}
});
そのため、PostRoute
(またはPostsPostRoute
を使用している場合はresource
)で、テンプレートはコントローラーのcontent
(Post
モデル)にアクセスできます。 、だから私は単にauthor
として著者を参照することができます
<script type="text/x-handlebars" data-template-name="posts/post">
<h3>{{title}}</h3>
<div>by {{author.fullName}}</div><hr />
<div>
{{body}}
</div>
{{partial comments}}
</script>
<script type="text/x-handlebars" data-template-name="_comments">
<h5>Comments</h5>
{{#each content.comments}}
<hr />
<span>
{{this.body}}<br />
<small>by {{this.author.fullName}}</small>
</span>
{{/each}}
</script>
( fiddle を参照)
非関連モデルの回答:
ただし、説明したシナリオよりもシナリオが少し複雑である場合、および/またはhave特定のルートに異なるモデルを使用(またはクエリ)する場合は、実行することをお勧めしますin Route#setupController
。例えば:
App.PostsPostRoute = Em.Route.extend({
model: function(params) {
return App.Post.find(params.post_id);
},
// in this sample, "model" is an instance of "Post"
// coming from the model hook above
setupController: function(controller, model) {
controller.set('content', model);
// the "user_id" parameter can come from a global variable for example
// or you can implement in another way. This is generally where you
// setup your controller properties and models, or even other models
// that can be used in your route's template
controller.set('user', App.User.find(window.user_id));
}
});
そして今、Postルートにいるとき、私のテンプレートはuser
フックで設定されたようにコントローラーのsetupController
プロパティにアクセスできます:
<script type="text/x-handlebars" data-template-name="posts/post">
<h3>{{title}}</h3>
<div>by {{controller.user.fullName}}</div><hr />
<div>
{{body}}
</div>
{{partial comments}}
</script>
<script type="text/x-handlebars" data-template-name="_comments">
<h5>Comments</h5>
{{#each content.comments}}
<hr />
<span>
{{this.body}}<br />
<small>by {{this.author.fullName}}</small>
</span>
{{/each}}
</script>
( fiddle を参照)
Em.Object
を使用して複数のモデルをカプセル化することは、model
フック内のすべてのデータを取得するための良い方法です。しかし、ビューのレンダリング後にすべてのデータが準備されていることを保証することはできません。
別の選択肢は、Em.RSVP.hash
を使用することです。いくつかの約束を組み合わせて、新しい約束を返します。すべての約束が解決された後に解決された場合、新しい約束。そして、setupController
は、promiseが解決または拒否されるまで呼び出されません。
App.PostRoute = Em.Route.extend({
model: function(params) {
return Em.RSVP.hash({
post: // promise to get post
comments: // promise to get comments,
user: // promise to get user
});
},
setupController: function(controller, model) {
// You can use model.post to get post, etc
// Since the model is a plain object you can just use setProperties
controller.setProperties(model);
}
});
このようにして、ビューのレンダリングの前にすべてのモデルを取得します。また、Em.Object
を使用してもこの利点はありません。
もう1つの利点は、promiseとnon-promiseを組み合わせることができることです。このような:
Em.RSVP.hash({
post: // non-promise object
user: // promise object
});
Em.RSVP
の詳細については、こちらをご覧ください: https://github.com/tildeio/rsvp.js
ただし、ルートに動的セグメントがある場合は、Em.Object
またはEm.RSVP
ソリューションを使用しないでください
主な問題はlink-to
です。モデルでlink-to
によって生成されたリンクをクリックしてURLを変更すると、モデルはそのルートに直接渡されます。この場合、model
フックは呼び出されず、setupController
でモデルlink-to
を取得します。
例は次のとおりです。
ルートコード:
App.Router.map(function() {
this.route('/post/:post_id');
});
App.PostRoute = Em.Route.extend({
model: function(params) {
return Em.RSVP.hash({
post: App.Post.find(params.post_id),
user: // use whatever to get user object
});
},
setupController: function(controller, model) {
// Guess what the model is in this case?
console.log(model);
}
});
そしてlink-to
コード、投稿はモデルです:
{{#link-to "post" post}}Some post{{/link-to}}
ここで面白くなってきます。 url /post/1
を使用してページにアクセスすると、model
フックが呼び出され、setupController
がpromiseの解決時にプレーンオブジェクトを取得します。
ただし、link-to
リンクをクリックしてページにアクセスすると、post
モデルがPostRoute
に渡され、ルートはmodel
フックを無視します。この場合、setupController
はpost
モデルを取得しますが、もちろんユーザーを取得することはできません。
そのため、ダイナミックセグメントのあるルートでは使用しないでください。
しばらく_Em.RSVP.hash
_を使用していましたが、私が遭遇した問題は、レンダリングする前にすべてのモデルがロードされるまでビューを待機させたくないということでした。しかし、 Novelys の人々のおかげで Ember.PromiseProxyMixin :
3つの異なる視覚セクションがあるビューがあるとします。これらの各セクションは、独自のモデルに裏打ちされている必要があります。ビューの上部にある「スプラッシュ」コンテンツを支えるモデルは小さく、すぐにロードされるため、通常どおりロードできます。
ルートを作成_main-page.js
_:
_import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('main-stuff');
}
});
_
次に、対応するHandlebarsテンプレート_main-page.hbs
_を作成できます。
_<h1>My awesome page!</h1>
<ul>
{{#each thing in model}}
<li>{{thing.name}} is really cool.</li>
{{/each}}
</ul>
<section>
<h1>Reasons I Love Cheese</h1>
</section>
<section>
<h1>Reasons I Hate Cheese</h1>
</section>
_
それで、あなたのテンプレートで、チーズとの愛/憎しみの関係について別々のセクションを持ち、それぞれが(何らかの理由で)独自のモデルに裏付けられているとしましょう。各モデルには多くのレコードがあり、それぞれの理由に関連する詳細が記録されていますが、一番上のコンテンツをすばやくレンダリングしたいと考えています。これが_{{render}}
_ヘルパーの出番です。テンプレートを次のように更新できます。
_<h1>My awesome page!</h1>
<ul>
{{#each thing in model}}
<li>{{thing.name}} is really cool.</li>
{{/each}}
</ul>
<section>
<h1>Reasons I Love Cheese</h1>
{{render 'love-cheese'}}
</section>
<section>
<h1>Reasons I Hate Cheese</h1>
{{render 'hate-cheese'}}
</section>
_
次に、それぞれのコントローラーとテンプレートを作成する必要があります。この例では実質的に同一であるため、使用します。
_love-cheese.js
_というコントローラーを作成します。
_import Ember from 'ember';
export default Ember.ObjectController.extend(Ember.PromiseProxyMixin, {
init: function() {
this._super();
var promise = this.store.find('love-cheese');
if (promise) {
return this.set('promise', promise);
}
}
});
_
ここでPromiseProxyMixin
を使用していることに気付くでしょう。これにより、コントローラーが約束を認識します。コントローラーが初期化されると、PromiseはEmber Data。を介して_love-cheese
_モデルをロードする必要があることを示します。このプロパティはコントローラーのpromise
プロパティ。
次に、_love-cheese.hbs
_というテンプレートを作成します。
_{{#if isPending}}
<p>Loading...</p>
{{else}}
{{#each item in promise._result }}
<p>{{item.reason}}</p>
{{/each}}
{{/if}}
_
テンプレートでは、約束の状態に応じて異なるコンテンツをレンダリングできます。ページが最初に読み込まれると、[I Love Cheese]セクションに_Loading...
_と表示されます。 promiseがロードされると、モデルの各レコードに関連付けられたすべての理由がレンダリングされます。
各セクションは独立して読み込まれ、メインコンテンツのレンダリングをすぐにブロックしません。
これは単純な例ですが、他のみんなが私と同じように役に立つと思います。
コンテンツの多くの行に対して同様のことをしたい場合は、上記のNovelysの例がさらに関連していることがあります。そうでなかったら、上記はあなたのためにうまく働くはずです。
これはベストプラクティスではなく、単純なアプローチではないかもしれませんが、1つの中央ルートで複数のモデルを使用できるようにする方法を概念的に示しています。
App.PostRoute = Ember.Route.extend({
model: function() {
var multimodel = Ember.Object.create(
{
posts: App.Post.find(),
comments: App.Comments.find(),
whatever: App.WhatEver.find()
});
return multiModel;
},
setupController: function(controller, model) {
// now you have here model.posts, model.comments, etc.
// as promises, so you can do stuff like
controller.set('contentA', model.posts);
controller.set('contentB', model.comments);
// or ...
this.controllerFor('whatEver').set('content', model.whatever);
}
});
それが役に立てば幸い
他のすべての優れた回答のおかげで、私はここで最高のソリューションをシンプルで再利用可能なインターフェースに組み合わせたミックスインを作成しました。指定したモデルに対してafterModel
のEmber.RSVP.hash
を実行し、setupController
のコントローラーにプロパティを注入します。標準のmodel
フックと干渉しないため、それを通常どおり定義します。
使用例:
App.PostRoute = Ember.Route.extend(App.AdditionalRouteModelsMixin, {
// define your model hook normally
model: function(params) {
return this.store.find('post', params.post_id);
},
// now define your other models as a hash of property names to inject onto the controller
additionalModels: function() {
return {
users: this.store.find('user'),
comments: this.store.find('comment')
}
}
});
Mixinは次のとおりです。
App.AdditionalRouteModelsMixin = Ember.Mixin.create({
// the main hook: override to return a hash of models to set on the controller
additionalModels: function(model, transition, queryParams) {},
// returns a promise that will resolve once all additional models have resolved
initializeAdditionalModels: function(model, transition, queryParams) {
var models, promise;
models = this.additionalModels(model, transition, queryParams);
if (models) {
promise = Ember.RSVP.hash(models);
this.set('_additionalModelsPromise', promise);
return promise;
}
},
// copies the resolved properties onto the controller
setupControllerAdditionalModels: function(controller) {
var modelsPromise;
modelsPromise = this.get('_additionalModelsPromise');
if (modelsPromise) {
modelsPromise.then(function(hash) {
controller.setProperties(hash);
});
}
},
// hook to resolve the additional models -- blocks until resolved
afterModel: function(model, transition, queryParams) {
return this.initializeAdditionalModels(model, transition, queryParams);
},
// hook to copy the models onto the controller
setupController: function(controller, model) {
this._super(controller, model);
this.setupControllerAdditionalModels(controller);
}
});
https://stackoverflow.com/a/16466427/263757 は関連モデルに適しています。ただし、Ember CLIおよびEmber Dataの最近のバージョンでは、無関係なモデルに対してより簡単なアプローチがあります。
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Route.extend({
setupController: function(controller, model) {
this._super(controller,model);
var model2 = DS.PromiseArray.create({
promise: this.store.find('model2')
});
model2.then(function() {
controller.set('model2', model2)
});
}
});
model2
のオブジェクトのプロパティのみを取得する場合は、 DS.PromiseArray の代わりに DS.PromiseObject を使用します。
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Route.extend({
setupController: function(controller, model) {
this._super(controller,model);
var model2 = DS.PromiseObject.create({
promise: this.store.find('model2')
});
model2.then(function() {
controller.set('model2', model2.get('value'))
});
}
});
MilkyWayJoeの答えに加えて、ありがとうございます。
this.store.find('post',1)
返す
{
id: 1,
title: 'string',
body: 'string string string string...',
author_id: 1,
comment_ids: [1, 2, 3, 6],
tag_ids: [3,4]
};
著者は
{
id: 1,
firstName: 'Joe',
lastName: 'Way',
email: '[email protected]',
points: 6181,
post_ids: [1,2,3,...,n],
comment_ids: [1,2,3,...,n],
}
コメント
{
id:1,
author_id:1,
body:'some words and stuff...',
post_id:1,
}
...私は、完全な関係が確立されるように、リンクバックが重要であると信じています。それが誰かを助けることを願っています。
beforeModel
またはafterModel
フックは、動的セグメントを使用しているためにmodel
が呼び出されない場合でも常に呼び出されるため、使用できます。
非同期ルーティング docs:
モデルフックは、一時停止時の移行の多くのユースケースをカバーしていますが、場合によっては、関連するフックbeforeModelおよびafterModelのヘルプが必要になります。これの最も一般的な理由は、{{link-to}}またはtransitionTo(URLの変更による遷移ではなく)を介して動的URLセグメントを含むルートに遷移する場合、ルートのモデルが「への移行は既に指定されています(例:{{#link-to 'article' article}}またはthis.transitionTo( 'article'、article))。この場合、モデルフックは呼び出されません。これらの場合、ルータが遷移を実行するためにルートのすべてのモデルを収集している間に、beforeModelまたはafterModelフックを使用してロジックを格納する必要があります。
themes
にSiteController
プロパティがあるとします。次のようなものがあります。
themes: null,
afterModel: function(site, transition) {
return this.store.find('themes').then(function(result) {
this.set('themes', result.content);
}.bind(this));
},
setupController: function(controller, model) {
controller.set('model', model);
controller.set('themes', this.get('themes'));
}