科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道使用 Dojo 和 WebSphere Portal 实现客户端 Interportlet Communication

使用 Dojo 和 WebSphere Portal 实现客户端 Interportlet Communication

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文将描述如何使用 Dojo JavaScript 工具包将客户端 Interportlet Communication 添加到 IBM® WebSphere® Portal(以下称为 WebSphere Portal) Portlet。

作者:ibm 来源:ibm 2007年10月6日

关键字: 应用 技术 WEBSPHERE 中间件

  • 评论
  • 分享微博
  • 分享邮件

引言

许多门户开发人员已使用 WebSphere Portal 创建用于共享事件和数据以增强用户体验的协作 Portlet。例如,在一个 Portlet 中提交的选择可导致更新的信息在多个相关的 Portlet 中显示,从而有助于这些 Portlet 在其表示中保持同步。此功能在服务器上实现,依赖于 Portlet 将操作请求提交到服务器,以使该服务器可以在页面被刷新之前执行数据传输。

然而,当您将 Ajax 功能添加到 Portlet 时,此方法就变得无人问津了,因为 Ajax 功能使您可以动态更新由 Portlet 显示的数据,而不用提交操作请求或刷新页面。本文将说明如何使用 Dojo JavaScript 工具包在浏览器中共享 Portlet 之间的数据和事件,该工具包可为动态更新的 Portlet 提供协作支持。





回页首


什么是 Dojo?

Dojo 是开源 JavaScript 工具包,可用于开发动态 Web 应用程序。Dojo 的许多库中都包含 Ajax 支持、DOM 操作工具、事件处理系统和可自定义的小部件集。Dojo 将这些库与一个灵活的打包系统组合在一起,这样,您就可以只导入所需的库;Dojo 自动地为您解决了所有库与库之间的依赖关系。

使用 Dojo

动态打包系统正是使用 Dojo 库与使用传统 JavaScript 包含的区别所在。执行两个步骤就可以包括 Dojo 库。您将执行以下两个步骤:

  1. 包括 Dojo 引导。
    <script type="text/javascript" src="/path/to/dojo/dojo.js"> 
    </script>

  2. 标识您的应用程序所需要的 Dojo 库。您使用 dojo.require 语句来包括 Dojo 小部件的管理功能 (dojo.widget.*) 和 Button 小部件 (dojo.widget.Button)。
     <script type="text/javascript">
     dojo.require("dojo.widget.*");
     dojo.require("dojo.widget.Button");
     </script>

dojo.require 语句可指定您将要在应用程序中使用的 Dojo 资源。Dojo 引导将对页面进行扫描以查找这些 dojo.require 语句,并且将动态包括对应的库以使这些资源可供使用。动态加载进程还处理 Dojo 库之间的所有依赖关系,因此,您只需针对所使用的资源直接添加 require 语句。

结合使用 Dojo 和 WebSphere Portal

Dojo 的独特加载策略为 Portlet 开发人员出了一道难题。Dojo 引导 dojo.js 只应在 HTML 页面中包括一次。取决于浏览器类型,如果包括 dojo.js 两次或两次以上,则会导致 JavaScript 错误并向最终用户报告这些错误。在 WebSphere Portal 中避免出现该问题的最简单的方式是:在您的主题的 JSP 中包括 dojo.js,然后根据需要在您的主题和 Portlet JSP 中放置 dojo.require 语句。

然而,我希望读者在学习了示例应用程序后,能够在不需要修改其主题的情况下部署 Portlet。因此,我已在 Portlet 项目中包括 Dojo 库,并且用 JavaScript 编写了条件加载器。

条件加载器包含在 dojoLoader.jsp 文件中:

<script type="text/javascript">
	var path = "<%=request.getContextPath() %>/dojo/dojo.js";
	if(typeof dojo=="undefined") {
		document.write('<S');
		document.write('CRIPT type=\"text/javascript\" src=\"');
		document.write(path);
		document.write('\" ><\/S');
		document.write('CRIPT>');
	} 
</script>
 

此代码检查 dojo JavaScript 对象是否存在,如果未定义该对象,此代码将插入脚本,以便将 dojo.js 包括在当前 HTML 文档中。用于执行此操作的 JavaScript 代码不太好理解,因此,必须对字符串形式的 <script></script> 的所有实例进行拆分;否则,某些浏览器会尝试将其解析为脚本的实际的起始及结束语句。而采用修改主题 JSP 这一解决方案就会简单很多。因此,请只在无法修改主题 JSP 的情况下才使用此条件加载器技术。

使用 Dojo 事件

Dojo 包括一个事件通信系统,开发人员利用该系统可以连接应用程序的组件。传统 DOM 事件可将事件映射到任何属性、对象或元素,而该功能则超出了此范围;该功能包括了诸如面向方面的编程 (AOP) 或事件发布/订阅之类的高级功能。本文将重点讨论事件发布/订阅系统(称为主题),以及如何使用该系统以便在 Portlet 之间进行通信。

在 Dojo 事件发布/订阅系统中,可以基于共享主题或队列名称以匿名的方式在组件之间进行通信。JavaScript 组件可使用 Dojo 事件库来发布或订阅主题。发布组件可以使用与事件相关的数据创建对象,然后将所创建的对象发布到主题。此时将调用该主题的任何订阅者,所发布的对象在该调用中以参数的形式进行传递,从而使订阅者对新数据做出反应;例如,订阅者可执行异步调用并(或)更新其显示。

可以完整地将此设计映射到 Portlet 之间的客户端通信。由于 Portlet 应使用 <portlet:namespace> 标记将其 DOM 元素和 JavaScript 元素区别开来,因此,一个 Portlet 要从另一个 Portlet 的 HTML 页面中侦听 DOM 事件,就会非常困难。要解决此难题,您可以使用 Dojo 事件主题,这样,您就可以使多个 Portlet 对一个 Portlet 中的 DOM 事件做出响应,在此期间,这些 Portlet 仍能保持其各自的独立性。例如,动态 Portlet 可以执行异步调用以检索信息,然后使用事件主题向其他 Portlet 提供已更新的信息。

dojo.event.topic API 的总结

下文将概述 Dojo 主题 API,并描述如何使用各种方法以便在 JavaScript 组件之间进行通信。

dojo.event.topic.registerPublisher (String topic, Object obj, String funcName)
将函数注册为主题的发布者。在随后调用该函数时,就会将事件发布到该主题,函数的参数会传递到任何已在该主题上注册的侦听器。
dojo.event.topic.publish(String topic, Object message )
将所传递的对象手动发布到特定主题。然后,该对象将被传递到任何已在该主题上注册的侦听器。
dojo.event.topic.subscribe(String topic, Object obj, String funcName)
订阅特定主题的函数。在随后将事件发布到该主题时,将会创建一个针对已订阅函数的函数调用。侦听器在任何时候都可以订阅主题,即使在发布者未引用该主题的情况下也可以对其进行订阅。Dojo 在订阅者或发布者首次引用主题之时创建该主题。
dojo.event.topic.unsubscribe(String topic, Object obj, String funcName )
取消订阅特定主题的函数。
dojo.event.topic.destroy(String topic)
销毁该主题,并注销该主题的所有侦听器。

当您引用发布者或订阅者时,主题 API 将指定对象及函数名称。使用此方法的目的是为了使用 Dojo 小部件系统,以便可以注册特定的小部件实例。如果您不打算使用小部件,则可以选择将主题订阅者实现为 JavaScript 对象或实现为简单的函数。

在示例应用程序中,主题订阅者被实现为 JavaScript 对象。如果您更喜欢使用简单的 JavaScript 函数,将对象指定为 window 就可以使用简单的 JavaScript 函数。

例如:

dojo.event.topic.subscribe( "/myApp/myTopic", window, "myHandler");

此代码将会对 "/myApp/myTopic" 主题进行订阅。当将事件发布到该主题时,将会调用 JavaScript 函数 myHandler,所发布的事件在该调用中以参数的形式进行传递。





回页首


工作示例

让我们探讨一个简单的配置,该配置包括一个事件发布者 Portlet、两个侦听器 Portlet 和单个主题。所发布的事件对象将包含可供侦听器 Portlet 使用的两个数据字段。在 Portlet 之间进行通信的事件的序列如下所示:


图 1 示例 Portlet 事件流
图 1 示例 Portlet 事件流

  1. 用户在发布者 Portlet 上单击 Publish Event 按钮。此操作将创建 DOM 事件,该事件可触发 JavaScript 处理程序函数。
  2. 该处理程序使用 Dojo 库创建一个独立的事件对象,然后将该事件发布到主题。该主题的任何订阅者可接收这一新事件,而无需连接到原始的 Publish Event 按钮。
  3. 侦听器 Portlet 包含由 Dojo 调用的主题订阅函数及用于接收事件对象的主题订阅函数。这些函数使用事件中包含的信息对 Portlet 显示进行更新。

在示例中,发布者 Portlet 会记录 Publish Event 按钮被点击的次数。所发布的事件包含该计数及一条文本消息。每个侦听器 Portlet 将使用该事件消息中的一个元素更新其显示。侦听器 Portlet 在每次处理事件时,更新的区域将会发出闪光,以引起用户注意更改的地方。此闪光效果并不是事件发布/订阅过程中的一部分;添加此效果的目的是为了提高演示效果。

事件发布者 Portlet

需要在事件发布者 Portlet 中创建一个按钮,用户可单击该按钮对事件进行发布,并且需要在其中创建一个计数器以记录按钮的被点击次数。清单 1 显示了相关的 HTML/JavaScript 源代码,可使用这些源代码创建该按钮,添加处理程序函数以处理按钮点击事件,使计数器上的数字可以根据点击事件进行递增,并最终发布我们的事件。


清单 1. 事件发布者 Portlet 的源代码

<P>
The button below will publish a Dojo event to all registered subscribers.<br />
<button dojoType="Button" 
  widgetId="<portlet:namespace />_publishButton">Publish Event
</button>
</P>
<script type="text/javascript">
	// Load Dojo's code relating to widget managing functions
	dojo.require("dojo.widget.*");	
	// Load Dojo's code relating to the Button widget
	dojo.require("dojo.widget.Button");
	// Load Dojo's event handling functionality
	dojo.require("dojo.event.*");
	dojo.require("dojo.event.topic.*");

	// Register our init function
	dojo.addOnLoad( <portlet:namespace />_init ); 
	
	// Click counter
	var <portlet:namespace />_counter = 0;

	// Init function for this portlet's Dojo components
	function <portlet:namespace />_init() {
		var pubButton = dojo.widget.byId("<portlet:namespace />_publishButton");
		dojo.event.connect(pubButton,'onClick','<portlet:namespace />_onButton');
	}

	// Button onClick handler
	function <portlet:namespace />_onButton() {
		<portlet:namespace />_counter++;
		var evt = {
			count: <portlet:namespace />_counter,
			message: "Click message #" + <portlet:namespace />_counter
		};
		dojo.event.topic.publish("/myApp/myTopic", evt);
	}
</script>

阅读完上述代码后,您将了解到 dojoType 标记将 Publish Event 按钮标识为 Dojo Button 小部件。在 JavaScript 部分中,dojo.require 语句将标识该部件所使用的 Dojo 库。调用 dojo.addOnLoad() 时,会将 init 函数添加到函数列表中,页面 DOM 完成加载后,Dojo 将会调用此列函数。此技术与在 window.onLoad 处理程序中设置函数类似,唯一的不同之处在于:Dojo 处理多个 init 函数,这些函数都需要在文档完成加载之后运行。该功能在 Portal 应用程序中非常有用,因为该功能允许每个 Portlet 都指定一个 JavaScript init 函数,而不用担心 window.onLoad 处理程序会被覆盖。

使用 <portlet:namespace> 标记(来自标准 Portlet 标记库)对该 Portlet 中的 JavaScript 函数和变量设置了命名空间。该标记将被字母-数字字符串替代,该字符串是 Portlet 实例特有的,这可以防止在一个页面上组装多个 Portlet 时出现命名空间冲突问题。init 函数通过 Publish Event 按钮的 widgetId 对其进行定位,然后将该小部件的 onclick 事件连接到事件处理程序。事件处理程序将递增计数器,构造事件对象,并使用 dojo.event.topic.publish API 将该事件对象发布到名为 myApp/myTopic 的主题。事件对象具有以下两个元素:

  • count,计数器的值
  • message,文本消息(包含计数器)

Dojo 将事件对象传递到每个事件订阅者,然后,这些事件订阅者就可以对合适的内容进行处理。

事件订阅者 Portlet

需要在事件订阅者 Portlet 上创建一个显示字段,接收到所发布的事件时,将使用该事件的信息对该显示字段进行更新。订阅者 Portlet 还需要在主题上注册其事件处理函数。最方便的做法是,在 init 函数中执行该注册操作,该函数与事件发布者中使用的函数类似。清单 2 中显示了事件订阅者 Portlet 的 HTML/JavaScript 源代码。


清单 2. 事件订阅者 Portlet 源代码

<P>Button Click Count<br />
<span id="<portlet:namespace />_count" style="font-size : x-large;">0</span>
</P>
<script type="text/javascript">
  // Load Dojo's event handling functionality
  dojo.require("dojo.event.*");
  dojo.require("dojo.event.topic.*");
  dojo.require("dojo.lfx.html");
  dojo.require("dojo.gfx.color");

  dojo.addOnLoad( <portlet:namespace />_init ); 

  // Init function for this portlet's Dojo components
  function <portlet:namespace />_init() {
	<portlet:namespace />_myListener = new <portlet:namespace />_listener();
	<portlet:namespace />_myListener.load();
  }

  // event listener object
  function <portlet:namespace />_listener() {
    // Event handler - override for each portlet
    this.handleEvent = function(args) {
	  var countElement = document.getElementById("<portlet:namespace />_count");
	  countElement.innerHTML = args.count;
	  dojo.lfx.html.highlight("<portlet:namespace />_count",
	    dojo.gfx.color.named.blue,500).play();
     }
	    
    this.load = function() {
        dojo.event.topic.subscribe("/myApp/myTopic", this, this.handleEvent);
     }
  }
</script>

类似于事件发布者 Portlet,dojo.require 语句将指定该部件所需要的 Dojo 库,而 dojo.addOnLoad 将对 Portlet 的 init 函数进行注册。init 函数将创建一个新的事件侦听器对象,然后通过调用其加载方法向名为 /myApp/myTopic 的主题订阅该对象。当事件被发布到该主题时,将会调用侦听器对象的 handleEvent 方法。事件处理程序在显示消息计数的位置查找 DOM 元素,然后使用事件对象中传递的新值对该元素的 HTML 进行更新。

然后,处理程序将调用 dojo.lfx.html.highlight(),以实现可视化突出显示效果,从而使用户注意到已更新的区域。

事件应用程序包括另一个具有相同功能的事件订阅者 Portlet;该 Portlet 将显示事件消息,而不显示计数。两个侦听器从事件发布中接收同一对象,但在该对象中选择处理不同的成员。

运行示例

让我们看看示例应用程序,以了解这些 Portlet 如何一起工作。

  1. 如果您尚未下载相关内容,请下载示例 Rational Application Developer 7 的项目交换文件,该文件包含本文所讨论的 Portlet。
  2. 将该文件导入到 Application Developer。
  3. 要部署 Portlet,可导出 WAR 文件并在 WebSphere Portal 中部署该文件,或在集成测试环境中运行这些 Portlet。
  4. 新建一个测试页面,然后将这三个 Portlet 添加到这一新页面中。
  5. 要对应用程序进行测试,导航到在步骤 4 中创建的测试页面并单击 Publish Event 按钮即可。

每次单击该按钮时,页面将显示两个事件侦听器 Portlet,这两个事件侦听器 Portlet 会发出闪光并且发布者 Portlet 所发布的计数及消息会对其进行更新。


图 2. 示例 Portlet
图 2. 示例 Portlet




回页首


结束语

将 Ajax 功能添加到 Web 应用程序可极大地提高用户体验。在 WebSphere Portal 环境中,在 Portlet 中使用 Ajax 时可能会无法使用诸如协作 Portlet 之类的高级功能,因此,需要在两者之间做出适当的权衡然而,您可以使用 Dojo 工具包添加客户端 Interportlet Communication。Portlet 开发人员可以使用此技术创建动态的协作 Portlet,这些 Portlet 可以保持同步,同时还不用频繁地刷新页面,将刷新次数降至最少,并可提高响应能力。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章