通过阅读文档,您似乎必须(或应该)将模型分配给以下路线:
App.PostRoute = Ember.Route.extend({
model: function() {
return App.Post.find();
}
});
如果我需要在某个路径中使用多个对象怎么办?即帖子,评论和用户?我该如何告知加载这些路线?
Answers:
永远最后一次更新:我无法继续更新。因此,不建议使用此方法,并且可能采用这种方式。这是一个更好,更新的线程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>
(参见小提琴)
不相关模型的答案:
但是,如果您的方案比您描述的方案复杂一点,并且/或者必须针对特定的路线使用(或查询)不同的模型,那么我建议在中进行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));
}
});
现在,当我处于“发布”路线中时,我的模板将可以访问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
。但这不能确保视图渲染后准备好所有数据。
另一个选择是使用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
没有这个优势。
另一个优点是您可以将承诺和不承诺结合在一起。像这样:
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:
假设您的视图包含三个不同的可视部分。这些部分中的每一个都应具有自己的模型。支持视图顶部“ splash”内容的模型很小,可以快速加载,因此可以正常加载该模型:
创建一条路线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
这里使用,这使控制器能够感知承诺。初始化控制器后,我们指示应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}}
在模板中,您将能够根据承诺状态呈现不同的内容。最初加载页面时,将显示“我爱奶酪的原因”部分Loading...
。加载promise后,它将呈现与模型的每个记录相关的所有原因。
每个部分将独立加载,不会阻止主要内容立即呈现。
这是一个简单的示例,但是我希望其他所有人都认为它和我一样有用。
如果您希望对许多行内容执行类似的操作,则可能会发现上面的Novelys示例更加相关。如果不是这样,上面的方法对您来说很好。
这可能不是最佳实践,也不是幼稚的方法,但它从概念上说明了如何在一条中心路线上使用多个模型:
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);
}
});
希望能帮助到你
感谢所有其他出色的答案,我创建了一个mixin,将此处的最佳解决方案组合到一个简单且可重用的界面中。它执行的Ember.RSVP.hash
在afterModel
您指定的型号,然后注入的属性到控制器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/2637573适用于相关模型。但是,对于最新版本的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.PromiseObject而不是DS.PromiseArray:
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
编辑路线/视图,我想在其中渲染数据库中的所有现有标签,以便用户可以单击以将其添加到正在编辑的帖子中。我想定义一个代表这些标签的数组/集合的变量。您上面使用的方法是否可以做到这一点?
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
由于使用动态细分而未调用也是如此。
根据异步路由文档:
模型挂钩涵盖了用于承诺暂停过渡的许多用例,但是有时您需要相关挂钩的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(
方法内进行操作以获得所需的结果