如何在Ember.js中的控制器之间进行通信
我想创建一个页面,在左侧我有固定的视图(一些过滤器),它们适用于右侧的结果。如何在Ember.js中的控制器之间进行通信
例如,左侧是根据流派,标题,创建年份过滤电影的过滤器。 右侧是不同的图表和表格,根据所选过滤器进行更新。
所以我想在左边有一个固定的视图,然后在右边的一个根据路线变化的插座。
这是正确的方法吗?如果是这样,我无法弄清楚如何将过滤器更改传递给右侧的控制器。
这的jsfiddle显示了类似的设置:http://jsfiddle.net/DEHcK/2/
在ContentRoute - > setupController我得到NavigationController的实例,但我无法弄清楚如何让改变someValue中。
在上面的示例中,ContentController如何获取NavigationController中someValue的更改?
这是实现我在ember中描述的应用程序的正确方法吗?
的JavaScript:
window.App = Ember.Application.create();
App.Router.map(function() {
this.route('content');
});
App.ContentRoute = Ember.Route.extend({
setupController: function (controller) {
controller.set('navigationController', this.controllerFor('navigation'));
}
});
App.ContentController = Ember.ObjectController.extend({
navigationController: null,
observerOfSomeValue: function() {
this.set('observedValue', this.get('navigationController.someValue'));
}.observes('navigationController', 'navigationController.someValue'),
observedValue: null
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('content');
}
});
App.NavigationView = Ember.View.extend({
init: function() {
this._super();
this.set('controller', this.get('parentView.controller').controllerFor('Navigation'));
},
templateName: "navigation"
});
App.NavigationController = Ember.ObjectController.extend({
someValue: 'xx',
observedValue: null,
observerOfSomeValue: function() {
this.set('observedValue', this.someValue);
}.observes('someValue')
});
HTML:
<script type="text/x-handlebars" data-template-name="application" >
<div>
{{view App.NavigationView}}
</div>
<div>
{{ outlet }}
</div>
</script>
<script type="text/x-handlebars" data-template-name="navigation" >
Change {{view Ember.TextField valueBinding="controller.someValue"}}
<div>observed value in same view: {{controller.observedValue}}</div>
</script>
<script type="text/x-handlebars" data-template-name="content" >
<div style="margin-top: 2em">
observed value in another view: {{observedValue}}
</div>
</script>
我已经基本实现你以后已先行created you a JSFiddle。我认为这是值得一试的,尽管如此,你可以掌握Ember。
路由器
我们只是配置了IndexRoute
在这一点上,我们存储所有我们的歌曲为{{outlet}}
。
App.IndexRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', [
Ember.Object.create({ title: 'Stairway to Heaven', genre: 'Metal' }),
Ember.Object.create({ title: 'Ratts of the Capital', genre: 'Post Rock' }),
Ember.Object.create({ title: 'Wonderwall', genre: 'Brit Pop' }),
Ember.Object.create({ title: 'Last Flowers', genre: 'Indie Rock' })
]);
}
});
这段代码很可能会被某种类型的AJAX调用替换为您的后端Ruby/PHP。暂时,我们会给IndexRoute
负责设置控制器(因此setupController
)。这个责任也可能与控制器本身有关,并且抽象出AJAX调用可能是一个好主意,因为您会有许多类似的AJAX调用。
您也可以决定use Ember's DataStore,这将再次更改控制器的实施。
指数控制器
接下来,我们要建立我们的IndexController
(这是我们SongsController
真的),我们希望这个控制器有两个职责:
- 要存储的歌曲(和也许是为了从后端获取歌曲);
- 根据传入的过滤器来过滤歌曲。
为此,我们创建一个computed property来过滤掉内容,因为我们不想直接操作特殊的content
数组。
App.IndexController = Ember.ArrayController.extend({
content: [],
excludeGenres: [],
filteredContent: function() {
var excludeTheseGenres = this.get('excludeGenres').mapProperty('genre');
return this.get('content').filter(function(model) {
return Boolean(jQuery.inArray(model.get('genre'), excludeTheseGenres) === -1);
});
}.property('excludeGenres.length', 'content.length')
});
excludeGenres
将采用一组流派对象。例如,如果“Post Rock”包含在excludeGenres
之内,那么我们不会显示任何“Post Rock”相关歌曲,但如果它不存在,那么我们会向他们展示! IndexController
不负责维护这个数组,但它有责任过滤其内容,因为这个数组是已更新。
流派控制器
也许是最令人困惑的控制器在我们的小应用程序,因为它实际上并没有任何自己的内容,而是依赖于IndexController
的内容。
App.GenresController = Ember.ObjectController.extend({
needs: ['index'],
genres: function() {
return this.get('controllers.index').mapProperty('genre').uniq();
}.property('controllers.index.length'),
toggle: function(genreName) {
var indexController = this.get('controllers.index'),
genres = indexController.get('excludeGenres'),
genre = indexController.findProperty('genre', genreName);
if (genres.findProperty('genre', genreName)) {
genres.removeObject(genre);
return;
}
genres.pushObject(genre);
}
});
类型控制器的职责可以被定义为这样:
- 为了监测
content
阵列IndexController
的并获取特有的流派名称时其长度变化; - 当用户点击列表中的流派以包含/排除流派时,填充
excludeGenres
阵列。
要让称为toggle
方法,我们需要指定我们流派观点的动作来调用它单击时:<a {{action "toggle" genre}}>{{genre}}</a>
。现在,每当用户点击一个流派时,将调用toggle
方法,并将流派名称作为第一个参数传递。
一旦我们在toggle
方法中,它将确定流派是否已被排除。如果它被排除,那么它将被删除,反之亦然。一旦我们添加/删除了流派,filteredContent
computed属性将再次被触发,并且索引视图将被无缝更新。
GenresController
实际上并没有自己的内容的原因是当两者有关系时管理两个content
数组似乎很愚蠢。由于可以根据应用程序中存在的歌曲来确定流派,因此控制器可以从该列表中收集信息,并只需提取它所需的信息 - 在我们的案例中,流派。
这样,如果歌曲被添加/删除,那么流派列表可以保持同步。
结论
我觉得然而回答你原来的问题是,是为了让控制器能够彼此进行通信,你需要指定它需要(与needs
)。
Wow @Wildhoney,帮助很大。非常感谢你。你的回复中有很多有用的东西。我也经历了大部分其他答案并学到了很多东西。 – 2013-02-14 10:47:25
我很高兴它帮助!有任何问题,请询问。 – Wildhoney 2013-02-14 13:32:01
needs
对于Ember控制器已被弃用,并在Ember 2.0中被删除 - 看看它的Deprecation Guide。
复制/粘贴在这里指导,而不是写:
Ember.Controller.extend({
needs: ['comments'],
newComments: Ember.computed.alias('controllers.comments.newest')
});
你应该写:
Ember.Controller.extend({
comments: Ember.inject.controller(),
newComments: Ember.computed.alias('comments.newest')
});
Ember.inject.controller()
会懒洋洋地查找该控制器为核心它分配了相同的名称以 - 在这种情况下,为comments
控制器。欲了解更多信息,请参阅the API documentation。
感谢@Wildhoney,这是一个更新的小提琴。 http://jsfiddle.net/DEHcK/3/ – 2013-02-14 10:36:07