分享

跨越边界: Ajax on Rails

 鱼非鱼 2007-03-28

2006 年 12 月 26 日

对 Ajax 这种使 Web 页面更具交互性的技术的大肆宣传已成过度之势。Ruby on Rails 框架和 Ajax 的完美集成所产生的力量在一定程度上促成了该框架的繁荣。本文旨在揭示:是什么使 Ajax on Rails 成为如此强大的组合。

跨越边界 系列之前的两篇文章(参见 参考资料)全面介绍了 Streamlined,这是 Rails 的辅助框架,该框架有效地利用 scaffolding 来快速生成简单的、使用 Ajax 的用户界面。除非您一直与世隔绝,不然您一定会知道 Ajax 是这样一种编程技术,它使用 XML、JavaScript 和 Web 标准来创建高度交互性的 Web 页面,正如您在 Google Maps 和大量其他站点上所看到的页面那样。许多读过 Streamlined 文章的读者都要求我描述一下 Ajax 在 Ruby on Rails 上的运行方式。本文全面介绍了两个简单的 Ajax 例子,延着这个思路介绍了 Ruby/Ajax 这一组合如此成功的原因。在本系列的下篇文章中,我将探究 JavaScript 这门编程语言。

Ajax 定义

Ajax 代表 Asynchronous JavaScript + XML。信息架构师 Jesse James Garrett 于 2005 年提出这一术语,该术语用来描述一门在夹缝中生存了近二十年的技术(参见 参考资料)。Ajax 的使用随即爆增,不论在图书馆、流行网站还是文献作品中都保持同步增长。

Ajax 重新定义了基本的浏览器使用模型。原模型一次呈现一个页面。Ajax 允许浏览器在页面更新的间隔同服务器进行交流。这样做的好处是带来更加丰富的客户体验,但却以增加复杂度为代价。Ajax 是这样运行的:使用 JavaScript 客户端库在客户机和服务器间发送 XML。Ajax 开发人员可以在任何时刻从客户机发送异步请求,因而在服务器处理这些请求时,用户交互可以继续进行。下面就是 Ajax 请求的流程:

关于本系列

跨越边界系列 文章中,作者 Bruce Tate 提出这样一种观点,即当今的 Java 程序员们可以通过学习其他方法和语言很好地武装自己。自从 Java 技术明显成为所有开发项目最好的选择以来,编程前景已经发生了改变。其他框架影响着 Java 框架的构建方式,从其他语言学到的概念也可以影响 Java 编程。您编写的 Python(或 Ruby、Smalltalk 等语言)代码可以改变编写 Java 代码的方式。

本系列介绍与 Java 开发完全不同的编程概念和技术,但是这些概念和技术也可以直接应用于 Java 开发。在某些情况下,需要集成这些技术来利用它们。在其他情况下,可以直接应用概念。具体的工具并不重要,重要的是其他语言和框架可以影响 Java 社区中的开发人员、框架,甚至是基本方式。

  1. 一个事件(如用户的鼠标点击或编程计时器的触发)启动一个 JavaScript 函数。

  2. JavaScript 函数为部分页面而不是整个页面创建一个请求。JavaScript 随后通过 HTTP 将该请求发送到 Web 服务器。

  3. 此 HTTP 请求调用服务器上的一个脚本,如 Rails 控制器方法或 Java? servlet。

  4. 该服务器脚本创建一个 XML 文档并将其返回给服务器。

  5. 在接收结果的同时,客户机异步处理创建、更新或删除部分 Web 页面,如列表元素、div 标记或图像。

所有 Ajax 应用程序都使用类似这种顺序的一种方法。例如,某个应用程序允许将字典中的单词与其定义一起保存。旧式的应用程序会强迫您用一个新的页面视图来编辑定义。Ajax 允许原地编辑,它用一个条目字段替换定义文本,然后用更新的定义来替换该表单。

Ajax 解决方案的组件是:

  • 客户端 JavaScript 库,用来管理异步请求。
  • 服务器端 JavaScript 库,用来处理进来的请求,并构造一个 XML 响应。
  • 客户端 JavaScript 库,用来处理生成的 XML。
  • 称作文档对象模型(DOM)的库,允许对现有 Web 页面进行更新。
  • 辅助例程,用来处理不可避免的 UI 和集成问题。

事件/请求/响应/替换模型是大多数 Ajax 应用程序的核心模型,但如果您刚接触 Ajax,您一定会对 Ajax 中大量的可用库和这些库之间巨大的差别感到惊讶不已。该领域中有许多 Ajax 框架,它们的功能常常重叠且没有确定的胜出者。单就 Java 市场而言,有许多库可用,包括 Echo、Dojo、DWR、Google Web Toolkit(GWT)、Java Web Parts、AjaxAnywhere、AjaxTags、Scriptaculous 和 Prototype。这些框架使用截然不同的方法。一些框架试图通过生成 JavaScript 代码的 Java 库来隐藏 JavaScript,如 GWT。另一些框架致力于使 JavaScript 更易使用。一些相当地全面,如 Dom4J,而另一些则仅着力于解决好一个小问题。由于有许多流行的新技术,解决方案之间互相割据的场面有时会很难驾驭,调试工具、UI 实践(如 Back 按钮)和明智的开发实践的实现非常缓慢。Java 平台上的 Ajax 库的力量源自其多样性。这也正是其缺点所在,因为多样性导致了难以决断、集成方面的顾虑和复杂性。

有了 Ruby on Rails,开发体验就显著不同了,这是由于两个原因。首先,Ruby on Rails 有一个核心的 Web 开发平台:Ruby on Rails。其次,到目前为止,大多数在 Rails 上的 Ajax 开发体验都围绕着两个核心框架:Scriptaculous 和 Prototype(参见 参考资料)。Rails 方法使用运行时代码生成和定制标记,这使您不必理会复杂的 JavaScript。是时候自己来实践了。如果您想要在学习本文的过程中编写代码的话,需要下载 Rails,也要下载必要的 Ajax 框架(参见 参考资料)。打开您的 Rails 环境,跟我一起来吧。





回页首


没有 Ajax 的简单的 Rails 应用程序

要使用 Rails 和 Ajax,就要创建一个空项目,并生成一个有两个方法的控制器。一个控制器控制简单的页面,另一个控制器建立一个 Ajax 响应。键入下列代码:

rails ajax
                        cd ajax
                        script/generate controller ajax show time

第一行和第二行代码生成一个 Rails 项目,并切换到新目录。第三行代码生成一个叫做 ajax 的控制器,并查看两个动作:showtime。清单 1 显示了该控制器的代码:


清单 1. 有两个空方法的控制器
                        class AjaxController < ApplicationController
                        def show
                        end
                        def time
                        end
                        end
                        

首先在不使用 Ajax 的情况下构建两个简单视图,然后用 Ajax 将这两个视图绑定到一起。编辑 app/views/ajax 中的 show.rhtml 视图,使它和清单 2 类似:


清单 2. 简单视图
                        <h1>Ajax show</h1>
                        Click this link to show the current <%= link_to "time", :action => "time" %>.
                        

清单 1 和清单 2 中的代码不支持 Ajax,但我还是会仔细分析该代码。首先,看清单 1 中的控制器。两个空的控制器方法处理进来的 HTTP 请求。如果不明确地呈现一个视图(使用 render 方法),Rails 会呈现和该方法同名的视图。由于 Scriptaculous 和 Prototype 库也使用 HTTP,Rails 不需要对标准 HTTP 方法和 Ajax 方法进行区分。

现在将注意力转移到清单 2 中的视图。大多数代码都是简单的 HTML 代码,只有第二行的 link_to 辅助例程例外:<%= link_to "time", :action => "time" %>

正如在跨越边界 之前的文章中所看到的那样,Ruby 用其表达式的值替代 <%=h%> 之间的代码。在这个示例中,link-to 方法是一个生成简单 HTML 链接的辅助例程。可以通过执行该代码看到该链接。通过键入 script/server 启动服务器,然后将浏览器指向 http://localhost:3000/ajax/show 。您将看到图 1 中的视图:


图 1. 不涉及 Ajax 的简单用户界面
不涉及 Ajax 的简单用户界面

在浏览器中,单击菜单项来查看页面源代码(在 Internet Explorer 为 View > Source ,在 Firefox 中为 View > Page Source)。您将看到清单 3 中的代码:


清单 3. 由 show.rhtml 生成的视图
                        <h1>Ajax show</h1>
                        Click this link to show the current <a href="/ajax/time">time</a>.
                        

请注意清单 3 中的链接代码。该模板让 Rails 用户不必面对冗长且容易出错的 HTML 句法。(Ajax 代码也是这样运行:使用辅助方法插入 JavaScript 代码,该代码替您管理远程请求和 HTML 替换。)如果单击该链接,将看到针对 time 方法的默认视图,但我还没有实现它。为加以补救,请用清单 4 中的代码替换 app/controllers/ajax_controller.rb 中的 time 方法。为保持简单,我直接从控制器中呈现视图。稍后,我会把一切处理好并呈现视图。


清单 4. 呈现时间
                        def time
                        render_text "The current time is #{Time.now.to_s}"
                        end
                        

现在,当单击该链接时,会得到图 2 中的视图:


图 2. 不涉及 Ajax 的视图
不涉及 Ajax 的简单视图

很快就能看到这个 UI 的一个问题。这两个视图不从属于单独的页面。该应用程序表示一个单一概念:单击一个链接来显示时间。为反复更新时间,每次都需要单击该链接和 Back 按钮。将该链接和时间放到相同的页面中也许可以解决这个问题。但如果该页面变得非常大或非常复杂,重新显示整个页面会很浪费,也会很复杂。





回页首


添加 Ajax

Ajax 让您可以只更新 Web 页面的一个片段。Rails 库为您处理大部分的工作。要将 Ajax 添加到这个应用程序中,需要以下四个步骤:

  1. 配置 Rails 以使用 JavaScript。
  2. 更改时间链接来提交 JavaScript Ajax 请求,而不是仅呈现一个 HTML 链接。
  3. 指定要更新的 HTML 片断。
  4. 为更新的 HTML 内容准备一个位置。
  5. 构建一个控制器方法,或者一个视图来呈现 Ajax 响应。

首先,更改 app/views/ajax/show.rhtml 中的代码,使其与清单 5 类似:


清单 5. 更改显示视图来使用 Ajax
                        <%= javascript_include_tag :defaults %>
                        <h1>Ajax show</h1>
                        Click this link to show the current
                        <%= link_to_remote "time",
                        :update => ‘time_div‘,
                        :url => {:action => "time"} %>.<br/>
                        <div id=‘time_div‘>
                        </div>
                        

我做了一些更改。首先,为处理配置,简单地将必要的 JavaScript 库直接包含在视图中。通常,还会有更多的视图,为避免重复,我将 JavaScript 文件包含在一个公共的 Rails 组件中,如 Rails 布局。本例只有一个视图,所以一切从简。

其次,我改变了链接标记来使用 link_to_remote。您一会儿就能看到这个链接的作用。请注意下列三个参数:

  • 链接文本:从非 Ajax 的例子中照搬过来。

  • :update 参数。如果您以前没见过这种语法,那么就把 :update => ‘time_div‘ 当作是一个已命名的参数,其中的 :update 是名称,update_div 是值。此代码告诉 Prototype 库:此链接中的结果将用 time_div 这一名称更新 HTML 组件。

  • 代码 :url => {:action => "time"} 指定该链接将调用的 URL。:url 从一个哈希映射表中获取值。在实际中,该哈希映射表只有一个针对控制器动作的元素::time。理论上,该 URL 也可以包含控制器的名称和控制器需要的任何可选参数。

在清单 5 中,还可以看到空的 div,Rails 将用当前时间更新它。

在浏览器中,装载页面 http://localhost:3000/ajax/show。单击该链接,将看到图 3 中的结果。


图 3. 含 Ajax 的视图
含 Ajax 的简单视图

为了很好地了解这里发生的情况,请查看该 Web 页面的源代码。清单 6 显示了该代码:


清单 6. 显示模板的结果(在启用 Ajax 的情况下)
                        <script src="/javascripts/prototype.js?1159113688" type="text/javascript"></script>
                        <script src="/javascripts/effects.js?1159113688" type="text/javascript"></script>
                        <script src="/javascripts/dragdrop.js?1159113688" type="text/javascript"></script>
                        <script src="/javascripts/controls.js?1159113688" type="text/javascript"></script>
                        <script src="/javascripts/application.js?1159113688" type="text/javascript"></script>
                        <h1>Ajax show</h1>
                        Click this link to show the current
                        <a href="#" onclick="new Ajax.Updater(
                        ‘time_div‘, ‘/ajax/time‘, {asynchronous:true, evalScripts:true});
                        return false;">time</a>.<br/>
                        <div id=‘time_div‘>
                        </div>
                        

请注意包含的 JavaScript 列表。Rails 辅助方法(include_javascript_tags :defaults)为您构建了此列表。接下来,看到一个用来构建新的 Ajax.Updater 对象的 JavaScript 函数调用,而不是一个 HTML 链接。正如您所料想的那样,名为 asynchronous 的参数被设置为 true。最后,在 HTML div 标记中看不到值,这是由于初始页面在那里没有值。





回页首


使用其他 Ajax 选项

Ajax 能生成强大的动作,甚至能生成一些预料不到的动作。例如,用户也许没注意到更新的时间链接。link_to_remote 选项让您能够轻易地将特殊效果应用到该条目上,从而让用户注意到该结果。现在将应用一些效果。请更改 show.rhtml 中的 link_to_remote 辅助方法,使它与清单 7 类似:


清单 7. 添加效果
                        <%= link_to_remote "time",
                        :update => ‘time_div‘,
                        :url => {:action => "time"},
                        :complete => "new Effect.Highlight(‘time_div‘)" %>
                        

最佳 Ajax 效果会使您的更改获得临时的关注,但却不会永久持续。您的目标应该是把更改提示给用户,而不打断他们的工作流。像这种用黄色来弱化强调的技术,或用滑入内容或淡出内容来让用户注意的技术都不是长久之计。

到目前为止,链接是您见到的惟一触发器。Ajax 还有许多其他的可用武器,一些由用户驱动,而另一些由程序事件驱动,如时钟。它是一个像闹钟一样并不需要用户干预的东西。可以用 Ajax 的 periodically_call_remote 方法定期更新时钟。请按照清单 8 编辑 show.rhtml 中的代码:


清单 8. 定期调用远程方法
                        <%= javascript_include_tag :defaults %>
                        <h1>Ajax show</h1>
                        <%= periodically_call_remote :update => ‘time_div‘,
                        :url => {:action => "time"},
                        :frequency => 1.0 %>
                        <div id=‘time_div‘>
                        </div>
                        

图 4 显示了结果:不需要用户干预,每隔一秒钟进行更新的时钟:


图 4. 用 Ajax 定期更新的时钟
用 Ajax 定期更新的时钟

尽管 Rails 视图中的代码和不含 Ajax 的版本相似,但背后的代码却很不同:此版本使用 JavaScript 代替了 HTML。可以通过在浏览器中查看源代码看到清单 9 中的代码:


清单 9. periodically_call_remote 的源代码
                        <script src="/javascripts/prototype.js?1159113688" type="text/javascript"></script>
                        <script src="/javascripts/effects.js?1159113688" type="text/javascript"></script>
                        <script src="/javascripts/dragdrop.js?1159113688" type="text/javascript"></script>
                        <script src="/javascripts/controls.js?1159113688" type="text/javascript"></script>
                        <script src="/javascripts/application.js?1159113688" type="text/javascript"></script>
                        <h1>Ajax show</h1>
                        <script type="text/javascript">
                        //<![CDATA[
                        new PeriodicalExecuter(function() {new Ajax.Updater(
                        ‘time_div‘, ‘/ajax/time‘, {asynchronous:true, evalScripts:true})}, 1.0)
                        //]]>
                        </script>
                        <div id=‘time_div‘>
                        </div>
                        

请一定注意这里发生的情况。您正在一个更高层的抽象之上有效地工作,而不是使用小块的自定义 JavaScript 片段,Ruby on Rails 模板系统使使用模型变得相当自然。

正如之前提到的那样,我正从控制器中直接呈现文本。这一简化使开始编程变得很容易,但却不能一直持续下去。视图应该处理显示,控制器应该在视图和模型间调度数据。这项设计技术叫做模型-视图-控制器(MVC),它使对视图或模型的更改更容易分隔开。为使这个应用程序符合 MVC,可以让 Rails 呈现默认视图,正如预料的那样,该内容将替代 time-div 之前的内容。请按照清单 10 更改 app/controllers/ajax_controller.rb 中的 time 方法:


清单 10. 重构
                        def time
                        @time = Time.now
                        end
                        

请按照清单 11 更改 app/views/ajax/time.rhtml 中的视图:


清单 11. 使用视图呈现 Ajax 内容
                        <p>The current time is <%=h @time %></p>
                        

控制器方法设置一个名为 @time 的实例变量。由于控制器什么都没明确地呈现出来,Rails 将 time.rhtml 视图呈现出来。这种使用模型和呈现一个不含 Ajax 的视图完全一致。 可以再一次看到,Rails 使开发人员不必考虑使用 Ajax 和不使用 Ajax 的应用程序间的区别。从传统的 Web 应用程序到 Ajax,该使用模型都惊人地相似。由于使用 Ajax 的成本如此之低,越来越多的 Rails 应用程序都开始利用 Ajax。





回页首


Rails 中 Ajax 的其他用法

Rails Ajax 体验领域宽广且内容深刻 —— 我无法用单篇文章甚至一系列文章来概括其深刻的内容。我只能指出 Rails Ajax 支持可以解决其他一些问题。下面是 Rails 中 Ajax 的一些通常用法:

  • 提交远程表单。 除了必须异步提交以外,Rails 中的 Ajax 表单和传统表单的执行方式完全一样。这意味着 Rails 中的 Forms 辅助标记让您必须指定一个要更新的 URL,执行可视化的效果,正如使用 link_to_remote 一样。正如 link-to-remote 扩展了 link_to 辅助方法一样,Rails submit_to_remote 扩展了一个 Rails submit 辅助方法。

  • 执行复杂脚本。 Rails 开发人员常常需要执行复杂的脚本,远不止更新单个 div 和执行效果那么简单。为此,Rails 提供 JavaScript 模板。用 JavaScript 模板,可以将任意 JavaScript 脚本作为 Ajax 请求的结果来执行。这些模板的一些常见用法(叫作 RJS 模板)为更新多个 div、处理表单验证和管理 Ajax 错误场景。

  • 拼写补全。 您一定想基于数据库中的条目为您的用户提供拼写补全服务。例如,如果用户键入 Bru,我想让我的应用程序注意到数据库中 “Bruce Tate” 这个值。可以使用 Ajax 定期检查字段的更改,并根据用户键入的内容发送拼写补全建议。

  • 动态构建复杂表单。 在业务领域里,常常需要查看部分已完成表单,然后才能知道用户应该完成哪个字段。例如,如果用户拥有一些特定种类的收入或费用,那么 1040EZ 纳税单是无效的。可以在这个过程中用 Ajax 更新表单。

  • 拖放。 可以用 Rails 快速实现拖放支持,这比大多数其他框架要省力得多。

 





回页首


结束语

Ajax 并不是没有问题。当 Ajax 运行良好时,整个体验会是激动人心的。但当运行不顺利时,您也许会发现对其进行调试是一个全新的领域,调试技术和工具还没有其他语言中那么成熟。Ruby on Rails 的确有一个核心优势:简单。Rails 包装器(加上很棒的社区支持)使得进入这一新环境变得简单,而且最初的投资非常低。但 Rails 支持也仅限于此。这两个框架还没有覆盖 Ajax 的全部内容,无法满足每个用户的需求。

Java 语言有更多的 Ajax 框架和方法可供选择。可以找到更具灵活性的,也可以找到具有很棒的支持基础的。但灵活性是要付出代价的。您不仅需要一个强大的 Ajax 框架,也需要一个 Web 开发框架。例如,集成 JSF 框架和集成 Struts 是两种截然不同的体验。新技术通常追求简单。对于那些在 UI 中需要 Ajax 的出色特性,却又不需要由 Java 提供的高级企业集成特性的问题,Ajax on Rails 也许正合适。下一次,我将更深入地介绍 JavaScript。请继续关注跨越边界。



参考资料

学习
  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文

  • Java To Ruby: Things Every Manager Should Know (Pragmatic Bookshelf,2006 年):这是本文作者编著的一本书,讲述何时何处从 Java 编程转到 Ruby on Rails 以及如何完成这种转变。

  • Beyond Java (O’Reilly,2005 年):本文作者编写的一本书,讲述 Java 语言的提高和稳步发展,以及在某些方面能够挑战 Java 平台的技术。

  • Book review: Agile Web Development with Rails ”(Darren Torpey,developerWorks,2005 年 5 月):这里介绍的这本书可以加深读者对 Rails 及敏捷开发方式原理的理解。

  • Ajax: A New Approach to Web Applications”(Jesse James Garrett,Adaptive Path,2005 年 2 月):“Ajax” 这一术语的发明者 Garrett 描述了其基本结构和用户体验特征。

  • ScriptaculousPrototype:增强 Rails Ajax 的两个 JavaScript 框架。

  • Rails Ajax helpers:这些辅助方法让 Rails 开发人员不必考虑低层 Ajax 编程的复杂性。

  • Ajax 技术资源中心:developerWorks Ajax 的门户,包含了专注于 Ajax 编程的内容、blog、论坛和其他资源。

  • Web 开发专区:developerWorks Web 开发方面的资源中心,包含了专注于 Web 和 Web 2.0 开发的文章、教程和其他资源。

  • Ajaxian:这是一个很棒的 Ajax 门户,它提供对所有 Ajax 相关内容的讨论。

  • Ajax BluePrints:对 Ajax 框架和 Java 编程语言设计技术的讨论。

  • Programming Ruby (Dave Thomas et al.,Pragmatic Bookshelf,2005 年):一本流行的关于 Ruby 编程的书。

  • Java 技术专区:数百篇关于 Java 编程各方面的文章。


获得产品和技术
  • Ruby on Rails:下载开放源码的 Ruby on Rails Web 框架。

  • Ruby:从该项目的 Web 站点获取 Ruby。


讨论


关于作者

Bruce Tate 居住在德克萨斯州的首府奥斯汀,他是一位父亲,同时也是山地车手和皮艇手。他是三本 Java 畅销书的作者,包括荣获 Jolt 大奖的 Better, Faster, Lighter Java。最近他又出版了 Beyond Java. 一书。他在 IBM 工作了 13 年,现在是 RapidRed 顾问公司 的创始人,在这里他潜心研究基于 Java 技术和 Ruby on Rails 的轻量级开发策略和架构。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多