ドキュメントを読むと、次のようにモデルをルートに割り当てる必要がある(または割り当てる必要がある)ようです。
App.PostRoute = Ember.Route.extend({
model: function() {
return App.Post.find();
}
});
特定のルートで複数のオブジェクトを使用する必要がある場合はどうなりますか?つまり、投稿、コメント、ユーザー?それらをロードするルートをどのように指示しますか?
回答:
永遠に最後の更新:これを更新し続けることはできません。したがって、これは非推奨であり、おそらくこのようになります。より良い、より最新のスレッドEmberJS:同じルートに複数のモデルをロードする方法は?
更新:私の元の回答embedded: true
では、モデル定義で使用すると言いました。それは正しくありません。リビジョン12では、Ember-Dataは、単一レコードまたはコレクションの外部キーがサフィックス(リンク)_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>
(フィドルを参照)
関連のないモデルで答える:
あなたのシナリオはあなたが説明したものよりも少し複雑で、かつ/または場合は、持っている特定のルートの使用(またはクエリ)異なるモデルに、私はそれを行うことをお勧めします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>
(フィドルを参照)
post.comments
。
Em.Object
複数のモデルをカプセル化するために使用することは、すべてのデータをmodel
フックするための良い方法です。ただし、ビューのレンダリング後にすべてのデータが準備されることを保証することはできません。
もう1つの選択肢は、を使用することEm.RSVP.hash
です。それはいくつかの約束を一緒に組み合わせて、新しい約束を返します。すべての約束が解決された後に解決された場合の新しい約束。そしてsetupController
、約束が解決または拒否されるまで呼び出されません。
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つの利点は、約束と非約束を組み合わせることができることです。このような:
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
モデルを取得しますが、もちろんユーザーを取得することはできません。
したがって、動的セグメントのあるルートでは使用しないでください。
controller.setProperties(model);
している場合は、これらのプロパティをデフォルト値でコントローラーに追加することを忘れないでください。それ以外の場合は例外がスローされますCannot delegate set...
しばらく使用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');
}
});
次に、対応するハンドルバーテンプレートを作成できます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>
次に、それぞれのコントローラーとテンプレートを作成する必要があります。この例では実質的に同じなので、1つだけ使用します。
と呼ばれるコントローラーを作成します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がlove-cheese
EmberDataを介してモデルをロードする必要があることを示します。このプロパティは、コントローラーのpromise
プロパティで設定する必要があります。
次に、love-cheese.hbs
:というテンプレートを作成します。
{{#if isPending}}
<p>Loading...</p>
{{else}}
{{#each item in promise._result }}
<p>{{item.reason}}</p>
{{/each}}
{{/if}}
テンプレートでは、約束の状態に応じてさまざまなコンテンツをレンダリングできます。ページが最初に読み込まれると、「チーズが大好きな理由」セクションが表示されます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);
}
});
それが役に立てば幸い
他のすべての優れた回答のおかげで、ここで最高のソリューションをシンプルで再利用可能なインターフェイスに組み合わせたミックスインを作成しました。指定したモデルに対してEmber.RSVP.hash
inafterModel
を実行してから、のコントローラーにプロパティを挿入し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')
}
}
});
これがミックスインです:
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/2637573は、関連するモデルに適しています。ただし、最近のバージョンのEmberCLIとEmberDataでは、関連のないモデルに対してより簡単なアプローチがあります。
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'))
});
}
});
post
DB内の既存のすべてのタグをレンダリングして、編集中の投稿にクリックして追加できるようにする編集ルート/ビューがあります。これらのタグの配列/コレクションを表す変数を定義したいと思います。上記で使用したアプローチはこれに有効ですか?
PromiseArray
(「タグ」など)を作成します。次に、テンプレートで、対応するフォームの選択要素にそれを渡します。
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: 'MilkyWayJoe@example.com',
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
。
あたりとして非同期ルーティングドキュメント:
モデルフックは、pause-on-promiseトランジションの多くのユースケースをカバーしていますが、関連するフック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'));
}
this
promise内で使用すると、エラーメッセージが表示されると思います。var _this = this
戻る前に設定してから、メソッド_this.set(
内で実行then(
して、目的の結果を得ることができます