86

ドキュメントを読むと、次のようにモデルをルートに割り当てる必要がある (または割り当てる必要がある) ようです。

App.PostRoute = Ember.Route.extend({
    model: function() {
        return App.Post.find();
    }
});

特定のルートで複数のオブジェクトを使用する必要がある場合はどうすればよいですか? つまり、投稿、コメント、ユーザー? それらをロードするようにルートに指示するにはどうすればよいですか?

4

8 に答える 8

117

最終更新は永久に : これを更新し続けることはできません。したがって、これは推奨されておらず、おそらくこのようになります。これは、より優れた最新のスレッドEmberJS: How to load multiple models on the same route? です。

更新: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.hasManyCommentDS.belongsToUserCommentPost

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あるコントローラーの にアクセスできるため、単純に次のように作成者を参照できます。Postauthor

<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>

フィドルを参照)

于 2013-05-09T16:19:18.463 に答える
49

を使用して複数のモデルをカプセル化することは、すべてのデータをフックEm.Objectする良い方法です。modelただし、ビューのレンダリング後にすべてのデータが準備されることを保証することはできません。

もう 1 つの選択肢は、 を使用することEm.RSVP.hashです。複数のプロミスを組み合わせて、新しいプロミスを返します。すべてのプロミスが解決された後に解決された場合、新しいプロミス。AndsetupControllerは、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. モデルで生成されたリンクをクリックして URL を変更するlink-toと、モデルはそのルートに直接渡されます。この場合、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フックが呼び出され、setupControllerpromise が解決されたときにプレーン オブジェクトが取得されます。

link-toただし、リンクをクリックしてページにアクセスすると、postモデルが渡されPostRoute、ルートはmodelフックを無視します。この場合setupControllerpostモデルを取得しますが、もちろんユーザーは取得できません。

そのため、動的セグメントを含むルートでそれらを使用しないようにしてください。

于 2013-10-19T14:10:08.180 に答える
13

しばらくの間 を使用してEm.RSVP.hashいましたが、レンダリング前にすべてのモデルがロードされるまでビューを待機させたくないという問題に遭遇しました。しかし、 Ember.PromiseProxyMixinを利用することを含む、 Novelysの人々のおかげで、私は素晴らしい (しかし比較的知られていない) 解決策を見つけました。

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>

次に、それぞれのコントローラーとテンプレートを作成する必要があります。この例では実質的に同じなので、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);
        }
    }
});

hereを使用していることに気付くでしょうPromiseProxyMixin。これにより、コントローラーは promise を認識します。コントローラーが初期化されると、promise がlove-cheeseEmber Data を介してモデルをロードする必要があることを示します。コントローラーのプロパティでこのプロパティを設定する必要がありpromiseます。

次に、次のテンプレートを作成しますlove-cheese.hbs

{{#if isPending}}
  <p>Loading...</p>
{{else}}
  {{#each item in promise._result }}
    <p>{{item.reason}}</p>
  {{/each}}
{{/if}}

テンプレートでは、promise の状態に応じてさまざまなコンテンツをレンダリングできます。ページが最初に読み込まれると、「チーズが好きな理由」セクションが表示されますLoading...。promise が読み込まれると、モデルの各レコードに関連付けられたすべての理由がレンダリングされます。

各セクションは個別に読み込まれ、メイン コンテンツのレンダリングがすぐにブロックされることはありません。

これは単純な例ですが、私と同じように他の人にも役立つことを願っています。

コンテンツの多くの行に対して同様のことをしようとしている場合は、上記の Novelys の例がより適切であることがわかるかもしれません。そうでない場合は、上記で問題なく動作するはずです。

于 2015-01-29T16:19:04.497 に答える
8

これはベスト プラクティスや単純なアプローチではないかもしれませんが、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);
  }
});

それが役に立てば幸い

于 2013-05-09T15:19:29.663 に答える
4

他のすべての優れた回答のおかげで、ここで最高のソリューションを組み合わせてシンプルで再利用可能なインターフェイスにする mixin を作成しました。Ember.RSVP.hash指定したモデルに対してinを実行しafterModel、プロパティを in コントローラーに注入します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);
  }
});
于 2014-10-18T01:35:05.003 に答える
0

動的セグメントを使用しているために が呼び出されなくても、これらは常に呼び出されるため、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'));
}
于 2014-02-22T06:19:14.550 に答える