原文:http://www.sitepen.com/blog/2011/11/07/communicating-between-widgets-in-a-rich-internet-application/
作者:Ken Franqueiro
译者:Liu Yang (fdliuyang@gmail.com)
作为我们十月(译者注:2011年)份举行的免费Dojo 支持活动的一部分,我们收到来自Manu Subramanian提出的关于如何在富互联网应用程序中管理多个不同控件之间通信的如下问题:
我们正在用dojo构建一个应用。它的有多个互相关联的界面组件。例如,页面顶部有一个包含多个下拉按钮的工具条,又例如菜单条目能够打开Tab 控制器的某个Tab。总之我们需要从不同的控件调用函数,或者说控件之间有非常多的相互联系。是不是应该创建一个大的控件来包含小的控件,并通过控件模板属性集合来来处理他们的相互联系? 在不久的将来,这方面,尤其是在“_TemplatedMixin”,是否会有任何变化?或者是否有更好的关于这方面的架构?
Manu,这是个非常好的问题!在构建需要包含多个需要相互通信的用户部件的应用程序时会碰到很多类似的问题。然而正确答案未必是唯一的,让我们来看看几种好的选择。
首先,让我们来重新检查我们的核心问题:我们有一个应用程序,其中某个区域的控件需要跟其他区域控件进行通信。然而,直接连接这两个控件的方法是非常脆弱的:它们其中的一个可能不总是存在,又有可能这个应用中某些部分将来会改变,如果直接将他们关联就会引起麻烦。因此,我们非常需要一个能够实现兴趣分离解耦的选择方案。这听起来就像是书刊发行和订阅的例子,Dojo通过dojo.publish和dojo.subscribe来提供了这样方案。
dojo.publish和dojo.subscribe实现了一个中心集线器的思想,它能够接收给定主题的消息(通过调用dojo.publish来发送的消息)。对消息感兴趣的一方能够通过调用dojo.subscribe来请求这些消息的通知,并设置一个回调函数。这样在每次dojo.publish被调用时,如果发送的是这些特定主题的消息,这个回调函数就会被调用。最重要的是这个方案中,不论发布者或者订阅者都不需要知道在程序中是谁发行或者谁订阅了同一主题。因此,程序中不会出现紧耦合或者上下文敏感的弊病。
现在我们理解了发行和订阅的概念,那我们如何真正的应用到我们的程序中呢?什么时候该调用发行和订阅呢?
关于发行主题,很明显的这应该是需要每个部件自己发行有关主题的,但这又会非常麻烦。在某些情况下,有可能通过构建简单的扩展就能化繁为简。举个例子,就拿菜单来说,如果要对每个菜单条目加一个onClickhandler的hook是非常费力的,但我们可以轻易的创建一个我们自己的菜单扩展类。我们从下面的小例子开始:
dojo.provide("my.PubMenu"); dojo.declare("my.PubMenu", dijit.Menu, { onItemClick: function(item, evt){ this.inherited(arguments); // don't forget that dojo.publish takes an array of arguments dojo.publish("/my/pubmenu/click", [item.id]); } });
这段代码定义了一个非常简单的模块,每当这个菜单实例的任何一个条目被点击的时候它就会发行一个事件。以下是一个测试的代码:
// create a menu instance var menu = new my.PubMenu({ id: "menu1" }); // generate some menu items for (var i = 1; i <= 5; i++) { menu.addChild(new dijit.MenuItem({ id: "item" + i, label: "Item " + i })); } // place or hook up where desired within application layout menu.placeAt(dojo.body()); menu.startup(); // for testing purposes: alert the id of the clicked item dojo.subscribe("/my/pubmenu/click", function(id){ alert("menu item clicked: " + id); });
可以用类似的模式来解决其他具有同一类型子类的控件的发行问题,比如工具条中的按钮,又或者甚至是树的节点。
接下来的问题是在哪里写订阅函数呢?同样的,有多种选择。可以在控件的逻辑代码中找到合适的位置来添加订阅的代码,尤其适用于如果每个区域由一个顶层模块控制的情况下。然而,从大局来看,如果过段时间再来看代码,又会发现这样很难找到某个消息究竟在哪里生效了,而且这会使得较大的或者经常性的重构变得困难。
一个可能的替代方案是建立一个控制器对象,让它来管理所有的订阅。这个控制器不一定要是一个成熟的dojo.declared的类,虽然如果你想让这个控制器的想法更进一步,并应用到具体的区域上,这也是可行的。
控制器的思想同样提供了另一种潜在的发行/订阅模式的替代方案:实际上,你可以选择将所有部件都通过控制器来订阅消息。关于这个话题,有个很好的教程——《应用控制器》,它可以给你一些很有意思的思路。你也许会对另外一个教程——《Dojo中的事件》感兴趣,它则关注与如何管理事件,连接和发行订阅的多种可行方案。
现在让我们来看看如何解决你的另一个关于模板控件的问题。在用户界面的区域上肯定会有模板控件或者编程控件的情况。模板控件常常在当有一类各自不同又有限的子控件情况下最有用。举例来说,在一个大的应用程序中,工具条中的菜单以及其他布局控件,它们的子节点很有可能依赖于某个配置或者某个外部因素来生成,因此是不可能事先定义的。这种情况最适合编程的方式来产生这些子节点。
例如,在前面提到的控制器教程例子中,你可能会发现并没有涉及定制的模板控件。用户界面的总体布局直接定义在应用程序的主页,而子控件(例如新的tab)是在控制器中用编程的方法创建的。
考虑到将来的变化,Dojo 1.7中dijit/_Templated 被dijit/_TemplatedMixin和dijit/_WidgetsInTemplateMixin取代了。当控件需要从模板中获益又不一定在模板中要解析子控件的情况下,工作量可能会减少。如果你想要对子控件模板化,你可以包含着两个mixin。
我们希望上面提出的这些方案能够启发你的思路。愿你在开发富互联网应用的旅程一切顺利。
SitePen 支持
请登录来 Best JavaScript and Dojo Support获得你想要了解的所有问题的答案。