如何在Emberjs中的控制器之间传递事件?更新:如何在Ember中设计自动完成

时间:2013-07-28 03:38:55

标签: ember.js

场景:一个自动完成小部件,它有自己的控制器和视图,因此它是一个模块化组件,可以使用{{render“autocomplete”}}或者可能是新的{{control}}帮助器呈现到任何其他视图中被移出实验阶段。为了使自动完成独立于其他所有内容,它不应该知道父控制器或选择项目时会发生什么操作。

我一直试图弄清楚如何使用Ember.Evented mixin,以便我可以触发自动完成的事件,例如itemSelected,然后可以冒泡并被父视图或控制器捕获。

与往常一样,缺乏Ember文档。特别是当谈到Ember.Evented。 (公平地说,文档已经走了很长一段路,但我仍然需要更多。他们说没有提供约定就会覆盖会议!)无论如何,足够的抱怨,文档显示ember偶然工作在通用对象上,我发现这个帖子进入了更深入一点:Does EmberJs support publish/subscriber eventing pattern?但我认为一个更真实的例子,包括控制器,视图,路径和自动填充等组件会很棒。

我认为这些类型的事件被认为是视图的责任但是没有用,所以我也将mixin放在控制器上。当你看到jsFiddle

时,这是有意义的

在其中你会看到一个假的自动完成控制器,它只是从一个灯具中获取一组静态内容,并显示每个项目的两个按钮,你可以点击它们从视图或控制器触发一个事件。在一个真实的例子中,将观察文本框中的输入,并且内容将被更新以基于输入呈现不同的建议,但这对于该示例并不重要。

jsFiddle:http://jsfiddle.net/wCfb9/ 我对这些内容最为怀疑:this.on("myEvent", this.addItem); 因为显然正在调用触发事件的函数,但就好像事件没有传播,或者索引控制器和视图实际上没有正确设置以响应事件。

以下是主要问题:

  1. 我需要更改哪些内容,以便索引控制器可以响应自动完成触发的事件?

  2. 实现这一目标的MVC最合适的方法是什么?触发/接收应该在视图/控制器上吗?

  3. 有更好的方法吗?

  4. 旁注: 我目前的解决方案是在自动完成控制器中添加needs: ["index"]而不是触发事件,我只是从自动完成控制器显式调用索引控制器中的方法。这有很多问题,首先是控制器之间的紧密耦合,如果自动完成被认为影响多个控制器或在其他地方重新使用并且必须重新配置等,则不能很好地扩展。

    希望我能够清楚地解释清楚。感谢所有帮助。

2 个答案:

答案 0 :(得分:3)

哇,好问题。

  

为了使自动完成独立于其他所有内容,它不应该知道父控制器或选择项目时会发生什么操作。

是的,我认为你在尽可能保持孤立的方面走在正确的轨道上。

  

有更好的方法吗?

我认为可行的方法是使用RC6中引入的新“组件”功能。有关详细信息,请参阅Ember Component API Docs

使用组件方法,您可以实现独立于父控制器的自动完成目标以及选择项目时会发生什么。它非常接近内置的Ember.Select视图的运行方式。例如:

{{app-autocomplete items=model selectedItem=selectedPerson}}

这里我设置了autocomplete的items和selectedItem属性来绑定到当前控制器上的值。 app-autocomplete不知道这些绑定的另一端是什么,或者当值发生变化时调用者将如何反应。然后,您的索引控制器可以观察它自己的selectedPerson属性,并在它发生更改时做出反应。像这样:

App.IndexController = Ember.ArrayController.extend({
  selectedPerson: null,
  selectedPersonDidChange: function() {
      Ember.Logger.log("Index.contoller addItem: ", this.get('selectedPerson').toString());
  }.observes('selectedPerson'),
});

该组件也非常简单。它期望它的items属性包含一个带有name的对象数组,当单击一个对象时,它会设置它自己的selectedItem属性。

<script type="text/x-handlebars" id="components/app-autocomplete">
  {{input}}
  {{#each item in items}}
    <li><button {{action itemSelected item}}>{{item.name}}</button></li>
  {{/each}}
</script>

App.AppAutocompleteComponent = Ember.Component.extend({
itemSelected: function (item) {
  this.set("selectedItem", item);
      Ember.Logger.log("component.itemSelected: myEvent triggered wit h: ", item.toString());
}
});

此处的完整示例:http://jsfiddle.net/dKUf4/

答案 1 :(得分:3)

根据Mike Grassotti的建议,我们认为Ember.Evented实际上不是正确的解决方案,并开始调查{{control}}和Ember.Component。

我仍然不确定他发布的jsFiddle链接,但我从他的帖子中获得了足够的信息并创建了一个jsBin来模拟使用{{control}}和使用Ember.Component的尝试。各有利弊,但最终,Ember.Component赢了,因为{{control}}没有正确绑定。

请参阅:http://jsbin.com/ehokak/3/ (我无法弄清楚如何在jsFiddle中设置ENV.EXPERIMENTAL_CONTROL_HELPER,这就是为什么我使用了jsBin而我最终更喜欢jsBin ......)

在其中您将看到两个自动填充。一个用{{control}}助手实现,它使用AutocompleteController和AutocompleteView,另一个用Ember.Component作为Mike Grassotti建议。

#1使用{{control}}帮助程序:

优点:我喜欢为自动完成设置控制器的想法,因为我认为这是一个更好的关注/单一责任分离。自动完成控制器应该知道如何处理输入框中的文本,如何发送请求以及在需要时解析数据。

缺点:我遇到的第一个问题是控件助手似乎需要一个模型。但是,索引控制器不应该关心自动完成使用的模型类型,它应该知道它将在我们选择绑定的属性上接收所选项。请参阅:controlItemSelectedBinding="itemSelected1"

第二个问题是一个显示阻止者,似乎控制助手的绑定不能按预期工作。因此,索引控制器从未在itemSelected1上观察到更改。我对如何在控件助手上编写绑定感到困惑,因为它们在组件上是不同的。有些人使用* Binding后缀,引用值等,我尝试了所有组合,但没有一个工作。我不确定这是一个错误还是一个功能,但它几乎就像绑定是单向的。

#2使用Ember.Component:

优点:实际上有效!正如您所看到的,当您在输入框中键入内容时,有一些虚拟代码可以更新内容,当您单击项目旁边的按钮时,它会设置本地componentItemSelected变量,因为它绑定到IndexControllers.itemSelected2,观察到更改并且索引控制器可以采取适当的操作。

缺点:我不喜欢组件负责处理模型等的想法,因为我的理解组件应该是视图的扩展,但考虑到没有实际的替代方案,这是默认的赢家

再次,非常感谢Mike关于使用Ember.Component的提示。