如何在EmberJS/Ember Data中使用单一路径的多个模型?

Ano*_*ous 85 ember.js ember-data

通过阅读文档,您似乎必须(或应该)将模型分配给路径,如下所示:

App.PostRoute = Ember.Route.extend({
    model: function() {
        return App.Post.find();
    }
});
Run Code Online (Sandbox Code Playgroud)

如果我需要在某个路线中使用多个物体怎么办?即帖子,评论和用户?如何告诉路由加载这些?

Mil*_*Joe 117

永远的最后更新:我不能继续更新这个.所以这已被弃用,可能会这样.这是一个更好的,更新的线程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]
}
Run Code Online (Sandbox Code Playgroud)

我已经更新了小提琴,如果有任何变化或者如果我发现此答案中提供的代码存在更多问题,我将再次这样做.


回答相关型号:

对于情况下,你的描述,我将依靠关联模型之间(设置embedded: true),并只加载Post模型,这条路线,考虑到我可以定义一个DS.hasMany用于关联Comment模型和DS.belongsTo协会User中都CommentPost车型.像这样的东西:

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')
});
Run Code Online (Sandbox Code Playgroud)

这个定义会产生如下内容:

模型之间的关联

有了这个定义,每当我find发布帖子时,我都可以访问与该帖子相关的评论集合,以及评论的作者,以及作为帖子作者的用户,因为它们都是嵌入的.路线保持简单:

App.PostsPostRoute = Em.Route.extend({
    model: function(params) {
        return App.Post.find(params.post_id);
    }
});
Run Code Online (Sandbox Code Playgroud)

所以在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>
Run Code Online (Sandbox Code Playgroud)

(见小提琴)


回答非相关模型:

但是,如果您的场景比您描述的更复杂,并且/或者必须使用(或查询)特定路线的不同模型,我建议您进行此操作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));
    }
});
Run Code Online (Sandbox Code Playgroud)

现在,当我在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>
Run Code Online (Sandbox Code Playgroud)

(见小提琴)

  • 非常感谢*因为花时间发布它,我发现它非常有用. (15认同)

dar*_*123 49

使用Em.Object封装多个模型是获取所有数据的好方法model.但它无法确保在视图渲染后准备好所有数据.

另一种选择是使用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);
  }
});
Run Code Online (Sandbox Code Playgroud)

这样,您可以在视图渲染之前获得所有模型.并且使用Em.Object没有这个优势.

另一个优点是你可以结合承诺和非承诺.像这样:

Em.RSVP.hash({
  post: // non-promise object
  user: // promise object
});
Run Code Online (Sandbox Code Playgroud)

检查这一点以了解更多信息Em.RSVP:https://github.com/tildeio/rsvp.js


但如果您的路线有动态段,请不要使用Em.ObjectEm.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);
  }
});
Run Code Online (Sandbox Code Playgroud)

link-to代码,帖子是一个模型:

{{#link-to "post" post}}Some post{{/link-to}}
Run Code Online (Sandbox Code Playgroud)

事情在这里变得有趣.当您使用url /post/1访问该页面时,将model调用该钩子,并setupController在promise解析时获取普通对象.

但是如果你通过点击link-to链接访问该页面,它会将post模型传递给PostRoute,并且路由将忽略model钩子.在这种情况下setupController会获得post模型,当然你无法获得用户.

因此,请确保不要在具有动态段的路径中使用它们.

  • 实际上有.如果要将模型ID而不是模型本身传递给link-to helper,则始终会触发模型钩子. (11认同)
  • 我的答案适用于早期版本的Ember&Ember-Data.这是一个非常好的方法+1 (2认同)
  • 这应记录在Ember指南的某处(最佳实践或其他内容).这是一个重要的用例,我相信很多人都会遇到. (2认同)

小智 13

有一段时间我正在使用Em.RSVP.hash,但我遇到的问题是我不希望我的视图等到所有模型在渲染之前加载.然而,由于Novelys的人们使用了Ember.PromiseProxyMixin,我找到了一个很棒的(但相对未知的)解决方案:

假设您有一个具有三个不同视觉部分的视图.这些部分中的每一部分都应该有自己的模型.支持视图顶部"splash"内容的模型很小,并且会快速加载,因此您可以正常加载该模型:

创建路线main-page.js:

import Ember from 'ember';

export default Ember.Route.extend({
    model: function() {
        return this.store.find('main-stuff');
    }
});
Run Code Online (Sandbox Code Playgroud)

然后,您可以创建相应的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>
Run Code Online (Sandbox Code Playgroud)

所以,让我们在你的模板中说你想要有关于你与奶酪的爱/恨关系的单独部分,每个(由于某种原因)由它自己的模型支持.每个模型中都有许多记录,其中包含与每个原因相关的详细信息,但是您希望顶部的内容能够快速呈现.这是{{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>
Run Code Online (Sandbox Code Playgroud)

现在,您需要为每个创建控制器和模板.因为它们在这个例子中实际上是相同的,所以我只使用一个.

创建一个名为控制器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);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

您会注意到我们正在使用PromiseProxyMixin此处,这使控制器可以识别承诺.初始化控制器时,我们指出承诺应该love-cheese通过Ember Data 加载模型.您需要在控制器的promise属性上设置此属性.

现在,创建一个名为的模板love-cheese.hbs:

{{#if isPending}}
  <p>Loading...</p>
{{else}}
  {{#each item in promise._result }}
    <p>{{item.reason}}</p>
  {{/each}}
{{/if}}
Run Code Online (Sandbox Code Playgroud)

在您的模板中,您将能够根据承诺状态呈现不同的内容.当您的页面最初加载时,将显示"我爱奶酪的原因"部分Loading....加载承诺后,它将呈现与模型的每条记录关联的所有原因.

每个部分将独立加载,不会阻止主要内容立即呈现.

这是一个简单的例子,但我希望其他人发现它和我一样有用.

如果您希望对许多内容行执行类似操作,您可能会发现上面的Novelys示例更具相关性.如果没有,上述应该适合你.


int*_*xel 8

这可能不是最佳实践和天真的方法,但从概念上说明了如何在一条中心路线上使用多种模型:

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);
  }
});
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你