7

场景:一个具有自己的控制器和视图的自动完成小部件,因此它是一个模块化组件,可以在移出后使用 {{render "autocomplete"}} 或新的 {{control}} 助手渲染到任何其他视图中的实验阶段。为了使自动完成独立于其他一切,它不应该知道父控制器或选择项目时发生的操作。

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

因此,与往常一样,缺少 Ember 文档。特别是在涉及 Ember.Evented 时。(公平地说,文档已经走了很长一段路,但我仍然想要更多。他们说约定优于配置而不提供约定!)无论如何,抱怨足够多,文档显示 ember 事件处理通用对象,我发现这篇文章进入更深入一点:EmberJs 是否支持发布/订阅者事件模式?但我认为一个更真实的例子,包括控制器、视图、路由和自动完成等组件会很棒。

我认为这些类型的事件应该是视图的责任,但这不起作用,所以我也将 mixin 放在控制器上。当您看到 jsFiddle 时,这将是有意义的

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

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

以下是主要问题:

  1. 我需要更改什么以便索引控制器可以响应自动完成触发的事件?

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

  3. 有一个更好的方法吗?

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

希望我解释得足够清楚。感谢所有帮助。

4

2 回答 2

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 的提示。

于 2013-07-29T23:12:19.147 回答
3

哇,好问题。

为了使自动完成独立于其他一切,它不应该知道父控制器或选择项目时发生的操作。

是的,我认为您在努力使事情尽可能孤立方面走在正确的轨道上。

有一个更好的方法吗?

我认为是的,要走的路是使用 RC6 中引入的新“组件”功能。有关详细信息,请参阅Ember 组件 API 文档

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

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

在这里,我将自动完成的项目和 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属性包含一个带有 a 的对象数组,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/

于 2013-07-29T06:03:14.807 回答