传统的 Ajax 开发依旧是实现富 Internet 应用程序(RIA)的最主要方法。但是,Adobe® Flex 的流行度不容忽视。本文将介绍 Adobe Flex Ajax Bridge (FABridge),这是让您可以采用轻松而一致的方法集成 Ajax 与 Flex 内容的代码库。阅读完本文后,您将能够利用通过 Flash 资源获得的丰富功能。
构建 Ajax 应用程序已被证明是提供优秀应用程序的一致方法。但是,Adobe Flex 的流行不容忽视。在不断努力实现最佳用户体验时,我们经常需要面对一项困难的任务:将嵌入到 Ajax 应用程序中的基于 Flash 的资源集成在一起。本文将讨论如何使用 FABridge 将 Flash 内容与现有 Ajax 内容集成在一起,FABridge 是由 Adobe 开发的代码库,用于处理这项特殊任务。
作为一名 Ajax 开发人员,现在是个特殊时刻。我们始终站在第一线,准备迎合用户并让他们对我们构建的应用程序留下最佳的第一印象。随着 Web 标准不断演进并且越来越多的供应商决定执行这些标准,我们的工作变得越来越轻松,使我们可以将精力集中在用户体验上。JavaScript 框架(例如 Ext JS、jQuery 和 Prototype)的进一步推进也使我们很少担心代码是否可以跨要求支持的平台工作,因此有更多时间进行创新。
虽然现在我们可以使用的工具、技术和资源的确越来越多,但是推动 Flash 开发的开发方法也在发生变化。对于许多工作室,开发流程将涉及用户界面(UI)组,从而实现支持在服务器端生成的应用程序的设计。使用现在已有的 JavaScript 框架,我们正朝着客户端应用程序开发的方向发展。但是,Flex 平台 — 生成 Flash 应用程序的免费开源框架 — 的出现将我们进一步引入应用程序开发的竞技场。这类创新带给我们的好处在于客户端,但是必须确保充分、仔细地处理它与当前架构的集成。
|
在介绍展示如何使用 Ajax 和 Flex 资源的代码样例之前,您必须了解必备的工具和技巧:
- 我使用 Ext JS JavaScript 库生成了本文中的 Ajax 样例,因此需要 下载包含库和支持文档的 .zip 文件。
- 接下来,获取拥有调试功能的 Adobe Flex 3 SDK 和 Adobe Flash Player 9 免费副本(如果您还没有的话)。
- 虽然本文不作要求,但是您至少还需要查看 Adobe Flex Builder 3 的试用版本,这是基于 Eclipse 的 IDE,除了高级调试和分析功能之外,您还可以快速地进行 Flex 应用程序开发(请参阅 参考资料)。
- 最后,了解 PHP 的工作知识十分有帮助。
如果您期望用 Flex 资源替代所有 Ajax 内容,您的任务将更简单一些。但是,这是一种不太可能而且通常不切实际的方法,因为经常需要保留传统的 Ajax 功能。幸运的是,您完全可以利用两种环境的优点来生成丰富、集成的应用程序。
将数据从 Flash 容器(HTML/JavaScript 代码)传递到 ActionScript 代码中有许多种简单方法,包括使用查询字符串和 <param>
标记。但是,这种方法仅限于将数据传递到容器中。一种功能更强大的技术是使用 ExternalInterface
类,这是一个应用程序编程接口(API),它用于代理 ActionScript 与 JavaScript 语言之间的通信。清单 1 中的示例最佳演示了 ExternalInterface
的使用:
清单 1. ExternalInterface 示例
// ActionScript code function exposed():String { return "Hello, JavaScript!"; } ExternalInterface.addCallback( "getActionScript", exposed ); // HTML/JavaScript code <script language="JavaScript"> var result = flashObject.getActionScript(); </script> <object id="flashObject" ...> <embed name="flashObject" ... /> </object> |
清单 1 演示了如何使用 ExternalInterface
类注册 ActionScript 函数,以便 JavaScript 代码可以调用该函数。您可以通过先定义 ActionScript 函数,然后使用 addCallback()
方法将该函数公开给要执行的 JavaScript 来完成操作。在 HTML 端,只需获得 Flash 容器的句柄并调用该函数,该函数是使用 addCallback()
方法的第一个参数命名的。虽然此演示主要是将函数公开给 JavaScript 代码,但是您可以使用另一种方式:使用 ExternalInterface
类的 call()
方法。
ExternalInterface
类可以十分强大,但是实现该类有很大的缺点。要使用 ExternalInterface
,必须可以编写代码以实现 ActionScript 和 JavaScript 环境。这不但要求额外的技巧,而且需要翻倍的工作量。在这种情况下,维护代码以及两个非常健壮的技能集可能会变得很困难。
为了解决针对 Flash 外部 API 的开发限制,Adobe 发布了 FABridge。Flex SDK 附带的 FABridge 是用于公开 Flash 内容以编写浏览器脚本的小型库,并且它可以在大多数主要浏览器平台中工作。使用 FABridge,现在实质上不再需要直接实现 Flash 外部 API 所需的管道代码。此外,实现桥接所需的技能也不再健壮。作为一名 JavaScript 开发人员,您只需能够了解哪些 ActionScript 属性和方法可用。让我们从演示 FABridge 功能的一些示例开始。
在开始使用 FABridge 之前,下面提供了可以使用的资料和开发环境。下载最新的 Flex SDK 后,请配置清单 2 中所示的目录结构:
清单 2. FABridge 教程的目录结构
/ +--- bridge | +--- fabridge.js | +--- fabridge.as | +--- index.html |
该目录结构非常简单:有一个索引页面和连接到 bridge 目录的 FABridge 脚本。FABridge 库文件的位置取决于您的环境。由于我使用的是 Mac OS X 中的 Flex Builder 3 Professional,因此我的库文件位于 install_root/sdks/frameworks/3.0.0/javascript/fabridge/ 中。
现在您已经准备好了合适的架构,您可以开始在 HTML/JavaScript 和 ActionScript 端创建骨架。使用清单 3 中的代码开发 HTML/JavaScript 骨架:
清单 3. HTML/JavaScript 骨架
<html> <head> <title>FABridge Tutorial</title> <script type="text/javascript" src="bridge/FABridge.js"></script> <script type="text/javascript"> // ... </script> </head> <body> </body> </html> |
正如您所见,只需将 FABridge JavaScript 库与代码链接在一起,就立即可以使用桥接的所有功能。接下来,使用清单 4 中的代码在 ActionScript 端实现桥接:
清单 4. 应用程序骨架
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:bridge="bridge.*" layout="absolute" width="300" height="142"> <bridge:FABridge bridgeName="flex" /> <mx:TextInput x="70" y="54" id="txt_test" text="FABridge rocks!"/> </mx:Application> |
您可能对这段代码更为陌生。通过定义带有 ID txt_test
和 FABridge rock 的默认值的单个文本输入控件,可以保持 UI 干净简单!定义了 bridge 名称空间,并且导入了 bridge 目录中的所有类。最后,给 Flex 应用程序命名以供桥接访问该应用程序时使用:flex。要将这段 Flex 代码编译到 SWF 工作文档中,请使用 Flex 3 SDK 中的 mxmlc 实用程序。清单 5 中显示了最基本的编译命令:
清单 5. 编译 MXML
path_to_flex_bin_folder/mxmlc path_to_mxml file |
清单 5 中的命令将编译源文件,并且在同一个目录中使用与 MXML 相同的名称输出 SWF 文件。假定编译成功,您现在可以将得到的 SWF 链接到 HTML 文件中,如清单 6 所示:
清单 6. 链接得到的 SWF 文件
<embed src=”main.swf” /> |
注:清单 6 中的代码主要用来演示 FABridge。除非您的目标是特定环境(清单 6 的目标环境是 Mozilla),否则需要在对象标记和其他加载脚本方面添加更多技巧。
假定一切运行正常,您的应用程序现在应当类似图 1:
图 1. 样例应用程序
现在您已经成功地编译并把 Flex 应用程序链接到 HTML 容器中,调用第一个 FABridge 函数以获得 Flex 应用程序的引用。使用清单 7 中的代码填充 HTML 骨架文件中的空 <script>
标记:
清单 7. 获得 Flex 应用程序的引用
// global variable, holds reference to the Flex application var flexApp; var initCallback = function() { flexApp = FABridge.flex.root(); return; } // register the callback to load reference to the Flex app FABridge.addInitializationCallback( "flex", initCallback ); |
清单 7 中的代码将首先定义一个全局 JavaScript 变量,当 FABridge 获得 Flex 应用程序的引用后,该变量将保存它。将定义一个回调函数,该函数将设置全局变量并且通过 addInitializationCallback()
FABridge 方法调用。使用这段代码只需要匹配在 Flex 应用程序中配置的桥接的名称。从这里开始,您可以从 JavaScript 代码访问各种 ActionScript 功能。
现在您已经获得了 Flex 应用程序的全局引用,您可以通过 FABridge 提供的一致接口访问 ActionScript 对象。在 ActionScript 世界中,您通常将通过点标记(dot notation)object.id
访问对象。但是,FABridge 不会在点标记中公开 ActionScript 对象,而是通过函数调用使这些对象可用。开始会觉得有点不同,但是您只需要知道要遵循的模板。在 ActionScript 中通常识别为 object.id
的对象现在可以作为 object.getId()
来访问。这最适合通过示例演示:将清单 8 中的代码键入到 HTML 骨架中,尝试一下:
清单 8. 通过 ID 获得 ActionScript 对象
// get the text input control var txt = flexApp.getTxt_test(); |
变量 txt
是用 Flex 应用程序中的 ID txt_test 表示文本输入控件的对象。您可以查看要按 ID 访问其他 ActionScript 对象所必须遵循的模板。该声明首先是 Flex 应用程序的全局引用,然后是始终以字符串 get 为开头、后接目标对象 ID 的方法调用。注意,此声明中的 ID 名称必须以大写字母为开头。
获得并设置 ActionScript 对象的属性与刚才的过程类似。承接处理文本输入控件的示例,使用清单 9 中的代码获得并设置 text
属性:
清单 9. 获得并设置 ActionScript 属性
alert( "old: " + txt.getText() ); txt.setText( "Reset!" ); alert( "new: " + txt.getText() ); |
清单 9 中的代码将首先警告 Flex 应用程序中的文本输入控件的初始值。通过遵循前述模板,您可以看到通过一个函数调用获得了 text
属性,前面附加 get 字符串,并且属性名应用驼峰匹配原则(camel cased)。set()
方法将使用相同的过程,但是将接受用于配置对象新值的参数。在执行了清单 9 中的代码后,您应当会看到类似于图 2 的屏幕:
图 2. 设置 ActionScript 对象属性
现在,让我们继续执行最简单的操作:调用 ActionScript 对象方法。此过程不需要特别注意。JavaScript 代码中使用了 ActionScript 对象方法,就像在 ActionScript 代码中使用这些对象方法一样。清单 10 中的代码演示了如何在文本输入控件中调用方法:
清单 10. 调用 ActionScript 方法
txt.setVisible( false ); |
清单 10 中的代码将把 Flex 应用程序中的文本输入控件设为不可见。仍然可以引用和处理该对象,只是在物理上不可见而已。在 ActionScript 与 JavaScript 之间,调用方法的方式没有改变。
FABridge 的最强大功能之一是能够在 JavaScript 和 ActionScript 代码之间传递函数。查看清单 11 中的代码,这段代码将把 Flex 中的文本输入值动态复制到 HTML/JavaScript 端的 <div>
中:
清单 11. 传递函数
// define a function used as a callback to JavaScript var txtCallback = function( event ) { // get the object which fired the event var swf_source = event.getTarget(); document.getElementById( "copy" ).innerHTML = swf_source.getText(); return; } txt.addEventListener( "change", txtCallback ); |
清单 11 中的代码是 JavaScript 回调函数,每次 Flex 应用程序中的文本输入控件值发生更改时都将触发该函数。当值更改时,将把该值连同 ID 一起复制到 <div>
标记中。此类功能非常强大,尤其是尝试在 Ajax 与 Flex 内容之间进行任何类型的集成时。由于两个环境都严重依赖事件,因此结合两者的能力很重要。
本文探究的最后一项功能是异常处理。默认情况下,在 JavaScript 代码中使用 try . . . catch
代码块,您至少能够访问错误代码,然后可以在 ActionScript 错误的联机参考中查找。这种方法肯定奏效,但是在开发期间,您会希望提前查看尽可能多的信息。在使用 FABridge 时,只需安装带有调试功能的 Flash Player 9,您就可以获得这些信息。安装了这项功能后,您可以访问行号、文件名、错误类型和堆栈跟踪。使用清单 12 中的代码查看一个示例:
清单 12. 异常处理
try { alert( flexApp.throwsAnError() ); } catch( e ) { var msg = ""; for( var i in e ) { msg += i + " = " + e[i]; } alert( msg ); } |
清单 12 中的代码抛出了一个错误,因为 throwsAnError()
方法不存在。catch
块中的代码将输出类似于图 3 的警告:
图 3. 异常数据
正如您所见,这些数据远比单个错误代码有用,并且所需的错误诊断工作也更加少。当您要处理诸如 JavaScript 和 ActionScript 之类的不同技术之间的复杂集成问题时,您会很庆幸自己得到了额外的帮助。
|
到目前为止,本文采用了教程类型的方法来说明 FABridge 的功能。现在该使用一个真实场景来演示它的实用性。如前述,您希望集成和使用 Ajax 和 Flex 领域各自的优点。实际上,Flex 中最吸引人的一个组件是制图功能。虽然它不是免费库,但是如果要执行大量客户端应用程序编程,则值得购买它。这里使用的示例是一个 PHP 服务的组合,提供了一些虚构的数据,其中包含针对特定用户接收的某些类型的消息的数量。来自 PHP 服务的数据将被载入到使用 Ext JS JavaScript 框架的网格控件中,然后作为数据提供者,通过 FABridge 将相同的数据传递给 Flex 中的饼状图。首先看一看清单 13 中的 PHP 服务:
清单 13. PHP 服务
<?php $users = array(); array_push( $users, "Paul" ); array_push( $users, "Nancy" ); array_push( $users, "Ned" ); array_push( $users, "Lucy" ); $email = array(); $email[ "email" ] = array(); $email[ "totalCount" ] = count( $users ); for( $i = 0; $i < count( $users ); $i++ ) { $tmp = array(); $tmp[ "user" ] = $users[$i]; $tmp[ "friends" ] = rand( 0, 100 ); $tmp[ "family" ] = rand( 0, 100 ); $tmp[ "spam" ] = rand( 0, 100 ); array_push( $email[ "email" ], $tmp ); } echo json_encode( $email ); ?> |
注:此服务中的数据均为硬编码,并且只是用于演示概念。
接下来,看看清单 14,这是用于生成饼状图的 MXML:
清单 14. Flex 饼状图
<?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:bridge="bridge.*" width="600" height="800"> <bridge:FABridge bridgeName="flex" /> <mx:Script> <![CDATA[ [Bindable] public var email:Object; private function displaySpam( data:Object, field:String, index:Number, percentValue:Number ):String { var temp:String= (" " + percentValue).substr(0,6); return data.user + ": " + '/n' + "Total Spam: " + data.spam + '/n' + temp + "%"; } ]]> </mx:Script> <mx:Panel title="Spam E-mail"> <mx:PieChart id="chart" height="100%" width="100%" paddingRight="5" paddingLeft="5" showDataTips="true" dataProvider="{email}"> <mx:series> <mx:PieSeries nameField="user" labelPosition="callout" field="spam" labelFunction="displaySpam"> </mx:PieSeries> </mx:series> </mx:PieChart> <mx:Legend dataProvider="{chart}"/> </mx:Panel> </mx:Application> |
这段代码是从 Adobe 文档中摘取出来的,经过修改以适应场景,并且经过配置以与 FABridge 结合使用。这里需要注意的是名为 email
的变量是可绑定的,这意味着将自动更新此数据集的任意引用。这样做非常合适,因为您将通过桥接把数据发送给这个相同的变量,然后使用该变量作为饼状图的数据提供者。
最后看一看清单 15 中的 JavaScript 代码:
清单 15. 生成网格,填充图表
<link rel="Stylesheet" type="text/css" href="../global/ext-2.0.2/resources/css/ext-all.css" /> <script type="text/javascript" src="bridge/FABridge.js"></script> <script type="text/javascript" src="../global/ext-2.0.2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../global/ext-2.0.2/ext-all-debug.js"></script> <script type="text/javascript"> Ext.onReady( function() { // global variable, holds reference to the Flex application var flexApp; var initCallback = function() { flexApp = FABridge.flex.root(); // load the data store, grid, populate the pie chart from JavaScript initUI(); return; } // register the callback to load reference to the Flex app FABridge.addInitializationCallback( "flex", initCallback ); function initUI() { // create a data store using the PHP service var ds_email = new Ext.data.JsonStore( { url: "service.php", root: "email", fields: [ "user", "family", "friends", "spam" ] } ); // when the data store loads, update the pie chart ds_email.load( { callback: function( r, o, s ) { // send an array of objects over the bridge to fill in the pie chart var arr_email = new Array(); for( var i = 0; i < r.length; i++ ) { var tmp = new Object(); tmp.user = r[i].data.user; tmp.family = r[i].data.family; tmp.friends = r[i].data.friends; tmp.spam = r[i].data.spam; arr_email.push( tmp ); } flexApp.setEmail( arr_email ); } } ); // create a populate the grid. var grid_email = new Ext.grid.GridPanel( { title: "E-mail Count", store: ds_email, columns: [ { header: "User", dataIndex: "user" }, { header: "Family", dataIndex: "family" }, { header: "Friends", dataIndex: "friends" }, { header: "Spam", dataIndex: "spam" } ], height: 300, width: 450, renderTo: "grid-email" } ); } } ); </script> |
关于这段代码,首先需要注意的是使这段代码正常工作所需的 Ext JS 资源的链接。在绑定 Ext 的默认样式和调试脚本后,配置 onReady
块。只有准备好完整的文档对象模型(Document Object Model,DOM)后,才能执行该代码块。您应当十分熟悉用于填充带有 Flex 应用程序引用的 flexApp
全局变量的代码。回调中添加了 initUI
函数的执行代码。此函数用于使用 PHP 服务创建 Ext 数据存储并使用其中的结果数据填充 Ext 网格控件。在加载 Ext 数据存储时,将创建并通过 FABridge 推动数据结构,以便使用该数据绑定作为饼状图的数据提供者。最终结果如图 4 所示:
图 4. 最终结果
仔细察看网格中的数据,它应当与饼状图中所示的数据相匹配。这实际上是一个强大的概念,并且您可以看到它提供的可能性。
虽然这是实现 FABridge 的一个实际示例,但是使用该库有若干种其他常见方法。为远程服务验证同步安全信息,以及提供品牌一致的技术,以及打造个性化应用程序,这些仅仅演示了桥接的一部分最佳应用。
|
Adobe Flex 是一项非常惊人的技术,它现在才刚刚开始崭露自己真正的潜力。但是,没有一个产品可以满足开发人员和用户的所有需求,因此保持开放式思维并探究使用 FABridge 集成的可能性至关重要。
学习
- 您可以参阅本文在 developerWorks 全球网站上的 英文原文。
- Adobe Flex 参考资料:查看 Adobe Flex 的文档集。
- Ext JS 参考资料:查找关于 Ext JS JavaScript 框架的文档和教程。
- 掌握 Ajax :阅读 developerWorks 系列获得 Ajax 的综合概述。
- 技术书店:浏览关于这些主题及其他技术主题的书籍。
- IBM 技术活动和网络广播:随时关注 developerWorks 的技术活动和网络广播。
- Ajax 技术资源中心:developerWorks 上所有有关 Ajax 的问题都可以在这里找到解答。
- developerWorks 开放源码专区:访问 developerWorks 开放源码专区,获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用。
获得产品和技术
- Adobe Flex:访问 Flex 产品页面。
- Ext JS:下载 Ext JS JavaScript 框架。
讨论
- Flex 论坛:参与 Flex 讨论。
- Ext JS 论坛:参与 Ext 讨论。
- developerWorks 博客:参与 developerWorks 博客并加入 developerWorks 社区。