從閱讀文檔,它看起來像你必須(或應該)模型分配給路由,像這樣:如何在EmberJS/Ember Data中使用單一路徑的多個模型?
App.PostRoute = Ember.Route.extend({
model: function() {
return App.Post.find();
}
});
如果我需要在一定的航線使用幾個對象?即帖子,評論和用戶?我如何告訴路線加載這些路線?
從閱讀文檔,它看起來像你必須(或應該)模型分配給路由,像這樣:如何在EmberJS/Ember Data中使用單一路徑的多個模型?
App.PostRoute = Ember.Route.extend({
model: function() {
return App.Post.find();
}
});
如果我需要在一定的航線使用幾個對象?即帖子,評論和用戶?我如何告訴路線加載這些路線?
末永遠更新:我無法繼續更新。所以這是不贊成的,可能會這樣。這裏有一個更好,更漂亮,更最新線程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
)之間associationsPost
模型,這條路線,考慮到我可以在Comment
和Post
模型中定義Comment
模型的DS.hasMany
關聯和User
模型的DS.belongsTo
關聯。事情是這樣的:
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);
}
});
在
所以(或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)
回答與非相關機型:
但是,如果你的情況是一點點比你所描述的更爲複雜和/或有使用(或查詢)不同型號特定的路線,我會建議在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));
}
});
,現在當我在郵政路線是,因爲它是在setupController
鉤設置我的模板將有機會獲得user
財產控制器:
<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)
這可能不是最好的做法,一個天真的方法,但它在概念上顯示你將如何去有一箇中央的路線可用於多個車型:
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數據優勢。對於模型不相關的一些場景,我得到了類似的東西。 – MilkyWayJoe 2013-05-09 22:45:13
使用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
生成的鏈接來更改網址,則模型會直接傳遞到該路線。 在這種情況下,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}}
事情變得有趣在這裏。當您使用網址/post/1
訪問該頁面時,將調用model
鉤子,並且當承諾解析時,setupController
將獲得普通對象。
但是,如果你通過點擊訪問該頁面link-to
鏈接,它傳遞post
模型和路線會忽略model
掛鉤。在這種情況下,setupController
將得到post
模型,當然你不能得到用戶。
因此,請確保您不要在具有動態細分的路線中使用它們。
我的回答適用於早期版本的Ember&Ember-Data。這是一個非常好的方法+1 – MilkyWayJoe 2013-10-20 12:18:02
因此,在動態細分中沒有解決方法? – beku8 2013-12-28 09:27:15
其實有。如果你想傳遞模型id而不是模型本身來鏈接到helper,模型鉤子總是被觸發。 – darkbaby123 2013-12-28 11:11:36
您可以使用beforeModel
或afterModel
鉤子,因爲這些鉤子總是被調用,即使沒有調用model
因爲您正在使用動態段。
由於每asynchronous routing文檔:
模型鉤涵蓋了暫停上承諾的過渡許多用例,但有時你需要的相關掛鉤beforeModel和afterModel的幫助。最常見的原因是,如果您通過{{link-to}}或transitionTo(而不是由URL更改導致的轉換)過渡到包含動態網址段的路線,則路線模型'轉換成將已經被指定(例如{{#鏈接到'文章'文章}}或this.transitionTo('文章',文章)),在這種情況下,模型鉤子不會被調用。在這些情況下,您需要使用beforeModel或afterModel掛鉤來存放任何邏輯,而路由器仍然收集所有路由的模型以執行轉換。
所以說,你有你的SiteController
一個themes
屬性,你可以有這樣的事情:
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'));
}
我認爲在promise中使用'this'會給出錯誤信息。你可以在返回之前設置'var _this = this',然後在'then'內執行'_this.set(''獲得所需結果的方法 – 2014-04-05 04:51:43
添加到MilkyWayJoe的回答,謝謝BTW:
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,
}
... 我相信,這樣建立了完整的關係鏈接背上有重要意義。希望能幫助別人。
感謝所有其他優秀的答案,我創建了一個mixin,將最好的解決方案結合到一個簡單且可重用的界面中。它對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')
}
}
});
這裏是混入:
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);
}
});
對於我是用Em.RSVP.hash
一段時間,但我跑進的是,我不希望我的觀點的問題等到渲染前加載所有模型。但是,由於Novelys中的人員使用了Ember.PromiseProxyMixin:
我發現了一個很好(但相對未知)的解決方案,假設您有一個視圖具有三個不同的視覺部分。這些部分中的每一部分應該由它自己的模型支持。該模型支持在視圖頂部的「撲通」含量小,可以快速載入,這樣你就可以加載一個正常:
創建路線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>
現在,你需要爲每一個控制器和模板。既然它們在這個例子中是完全相同的,我就用一個。
創建一個名爲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
這裏,這使得控制許感知。當控制器初始化時,我們表明承諾應該通過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}}
在模板中,你就可以根據承諾的狀態來呈現不同的內容。當您的頁面初次加載時,您的「我愛吃芝士的原因」部分將顯示Loading...
。當承諾被加載時,它將呈現與模型的每條記錄相關的所有原因。
每個部分將獨立加載,不會立即阻止主要內容的渲染。
這是一個簡單的例子,但我希望其他人都認爲它和我一樣有用。
如果您希望爲多行內容做類似的事情,您可能會發現上面的Novelys示例更加相關。如果沒有,上述應該適合你。
https://stackoverflow.com/a/16466427/2637573適用於相關型號。然而,隨着新版本的灰燼CLI和灰燼數據,對不相關的模型更簡單的方法:
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'))
});
}
});
非常感謝*花了很多時間發佈,我發現它非常有用。 – Anonymous 2013-05-09 18:07:51
@MilkyWayJoe,真的很棒!現在我的方法看起來很幼稚:) – intuitivepixel 2013-05-09 22:02:38
你的非相關模型的問題是它不接受像模型鉤那樣的承諾,對吧? 有沒有解決方法? – miguelcobain 2014-01-13 11:33:27