rails敏捷开发《2》.doc

上传人:一*** 文档编号:573522 上传时间:2018-10-30 格式:DOC 页数:87 大小:4.24MB
返回 下载 相关 举报
rails敏捷开发《2》.doc_第1页
第1页 / 共87页
rails敏捷开发《2》.doc_第2页
第2页 / 共87页
点击查看更多>>
资源描述

《rails敏捷开发《2》.doc》由会员分享,可在线阅读,更多相关《rails敏捷开发《2》.doc(87页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、.9.2 迭代 D2 :创建基于 Ajax 的购物车Iteration D2: Creating an Ajax-Based CartAjax 允许我们通过编写在浏览器中运行的代码,来与服务器端的应用程序交互。在这里,我们希望让 Add to Cart 按钮在后台调用服务器端的 add_to_card 方法,随后服务器把关于购物车的 HTML发回浏览器,我们只要把服务器更新的 HTML 片段替换到边框里就行了。要实现这种效果,通常的做法是首先编写在浏览器中运行的 JavaScript 代码,然后编写服务器端代码与 JavaScript 交互(可能是通过 JSON 之类的技术) 。好消息是,只要

2、使用 Rails,这些东西都将被隐藏起来:我们使用 Ruby(再借助一些 Rails 辅助方法)就可以完成所有功能。向应用程序中引入 Ajax 的技巧在于“小步前进” ,所以我们先从最基本的开始:修改货品列表页,让它向服务器端应用程序发起 Ajax 请求;应用程序则应答一段 HTML 代码,其中展示了最新的购物车。在索引页上,目前我们是用 button_to()来创建“Add to Cart”链接的。揭开神秘的面纱,button_to()其实就生成了 HTML 的标记。下列辅助方法:add_to_cart, :id = product %会生成类似这样的 HTML 代码这是一个标准的 HTML

3、 表单,所以,当用户点击提交按钮时,就会生成一个 POST 请求。我们不希望这样,而是希望它发送一个 Ajax 请求。为此,必须更直接地编写表单代码可以使用form_remote_tag 这个 Rails 辅助方法。 “form_._tag”这样的名字代表它会生成 HTML 表单,“remote”则说明它会发起 Ajax 远程调用。现在,打开 app/views/store 目录下的index.html.erb 文件,将 button_to()调用替换为下列代码:depot_l/app/views/store/index.html.erb :action = add_to_cart, :id

4、= product do %用:url 参数,你就可以告诉 form_remote_tag()应该如何调用服务器端的应用程序。该参数接收一个 hash,其中的值就跟传递给 button_to()方法的参数一样。在表单内部(也就是 do 和 end 之间的代码块中),我们编写了一个简单的提交按钮。在用户看来,这个页面就跟以前一模一样。虽然现在处理的是视图,但我们还需要对应用程序做些调整,让它把 Rails 需要用到的JavaScript 库发送到用户的浏览器上。在第 24 章“Web 2.0”(第 521 页)中,我们还会详细讨论这个话题;现在,我们只需在 store 布局的部分里调用 java

5、script_include_tag 方法即可。depot_l/app/views/layouts/store.html.erbPragprog Books Online Store“all“ % 到目前为止,浏览器已经能够向我们的应用程序发送 Ajax 请求,下一步就是让应用程序做出应答。我们打算创建一段 HTML 代码来代表购物车,然后让浏览器把这段 HTM 插入当前页面的 DOM1,替换掉当.前显示的购物车。为此,我们要做的第一个修改就是不再让 add_to_cart 重定向到首页。(我们知道,我们刚刚才加上这个功能,现在又要把它拿掉了我们很敏捷,对吧?) 我们调用 respond_to

6、()方法并告诉它我们要响应的是.js 格式文件 2。Download depot_l/app/controllers/store_controller.rbdef add_to_cartproduct = Product.find(params:id)cart = find_cartcart.add_product(product) respond_to do |format| format.js endrescue ActiveRecord:RecordNotFoundlogger.error(“Attempt to access invalid product #params:id“ )r

7、edirect_to_index(“Invalid product“ )end修改的结果是,当 add_to_cart 完成对 Ajax 请求的处理之后,Rails 就会查照 add_to_cart这个模板来执行渲染。在第 118 页,我们已经删掉了旧的.html.erb 模板,看起来现在又需要把它弄回来了。不过,还是让我们换另一种方式来做这件事吧。Rails 支持 RJS 模板的概念“JS”指的是 JavaScript。.js.rjs 模板可以将 JavaScript发送到浏览器,而你需要写的只是服务器端的 Ruby 代码。下面我们就来编写第一个.rjs 模板:add_to_cart.js.

8、rjs,它和别的模板一样,也位于 app/views/store 目录下。depot_l/app/views/store/add_to_cart.js.rjspage.replace_html(“cart“ , :partial = “cart“ , :object = cart)我们来分析一下这个模板。page 这个变量是 JavaScript 生成器的实例这是 Rails 提供的一个类,它知道如何在服务器端创建 JavaScript,并使其在浏览器上运行。在这里,我们希望它找到当前页面上 id 为 cart 的元素,然后将其中的内容替换成某些东西。传递给 replace_html 的参数看

9、上去很眼熟,因为它们就跟 store 布局中渲染局部模板时传入的参数完全一样。这个简单的 RJS 模板就会渲染出用于显示购物车的 HTML 代码,随后就会告诉浏览器将 id=“cart“的中的内容替换成这段 HTML 代码。这有效吗?在书里很难演示,不过它确实有效。首先刷新首页,以确保 form_remote_tag 调用和JavaScript 库被加载到浏览器。然后,点击 Add to Cart 按钮,就应该能看到边框里的购物车信息被更新了,同时浏览器却不会有任何刷新页面的迹象。你已经创建了一个 Ajax 应用程序。排疑解难Troubleshooting虽然 Rails 极大地简化了 Aja

10、x,却没办法让它变得易如反掌。而且,由于涉及到几种技术的松散整合,一旦 Ajax 出现故障,可能会很难跟踪调试。这也是应该始终小步前进、逐渐增加 Ajax 功能的原因。如果你的 Depot 应用没有显露出任何 Ajax 魔法,这里有几个提示: 你删除旧的 add_to_cart.html.erb 文件了吗? 你记得用 javascript_include_tag 将必要的 JavaScript 库包含到 store 布局中了吗? 你的浏览器是否有什么特殊的设置,强迫它每次都重新加载整个页面?有时候浏览器会在本地缓存页1 文档对象模型(Document Object Model)。这是文档结构和

11、内容在浏览器中的内部表示,我们据此来改变显示给用户的东西。2 这条语句乍看起来有些奇怪,其实就是一个使用代码块作为参数的方法调用。代码块在第 A.9 节“代码块与迭代器”中有介绍。在第 12.1 节“分别应答”中有更详细的描述。 .面内容,这也会给测试增加困难。也许你应该首先刷新整个页面,然后再进行测试。 是否收到了任何错误报告?请察看 logs 目录中的 development.log 文件。 还是日志文件,你是否看到了针对 add_to_cart 这个 action 的请求?如果没有,就表示你的浏览器并没有发起 Ajax 请求。如果 JavaScript 库已经加载到浏览器中(用 “查看源

12、文件”功能可以看到 HTML 源代码 ),是否你的浏览器禁用了 JavaScript? 有些读者告诉我们,当他们重新启动应用程序之后,基于 Ajax 的购物车就一切正常了。 如果你使用 Internet Explorer 浏览器,它有可能正运行在所谓 quirks 模式下这是为了兼容旧版本 IE 而设计的一种运行模式,它在处理 Ajax 时有很多问题。如果在页面上设置了适当的DOCTYPE 头信息,IE 就会转入 standards 模式,这种模式能够更好地处理 Ajax 内容。我们的布局模板中使用了下列头信息:客户永不满足The Customer Is Never Satisfied我们对自

13、己的表现感到相当满意:只修改了几行代码,原来那个乏昧的 Web1.0 应用就走上了 Web 2.0 和 Ajax 的康庄大道。屏住呼吸,把客户叫到身旁。什么都不用说,我们自豪地按下了 Add to Cart 按钮,然后扭头看着她,等待着她的赞扬。可是,没有赞扬,她看上去很惊讶。 “你们把我叫过来就是为了给我看一个 bug?”她问道, “你点了按钮,却什么都没发生。 ”我们耐心地解释说,其实背后已经发生好多事情了。看看边框里的购物车,看见了吗?我们添加货品的时候,购物车里的货品数量就从 4 变成了 5。“噢, ”她这样说, “我还真没注意到。 ”如果她注意不到页面更新的话,估计顾客也注意不到。看

14、来我们还需要对用户界面做点改进。9.3 迭代 D3 :高亮显示变化Iteration D3: Highlighting Changes前面就已经提到过,javascript_include_tag 辅助方法会把几个 JavaScript 库加载到浏览器中,其中之一的 effects.js 可以给页面装饰以各种有趣的可视化效果 3。在这些可视化效果中就包括如今声名显赫的“黄渐变技巧”(Yellow fade Technique)这是一种高亮显示页面元素的技巧,通常会首先把背景变成黄色,然后再渐变到白色。图 9.2 展示了将黄渐变技巧应用到我们的购物车上的效果:最后面的那张图片是最初的购物车;用户

15、点击 Add to Cart 按钮,购物车中货品的数量变成“2”,同时货品名称所在的那行文字背景变亮;随后,背景颜色又会逐渐变回原来的样子。我们来把这样的高亮效果加到购物车上:每当购物车被更新时(不管是添加了新的货品,还是改变了现有货品的数量),就把这部分的背景变亮。这样,用户就可以更清楚地看到“有东西发生了变化” ,虽然3 effect.js 是 script.aculo.us 库的一部分。在 http:/ 这里列出了一些可视化效果,你可以看看用这个库都能做些什么。.整个页面并没有刷新。我们面临的第一个问题是:如何知道购物车中的哪种货品是最近更新的?现在,每种货品在显示时都只是一个简单的 元

16、素,我们需要找到一个办法来标记出其中最近更新的一个。为此,首先需要对Cart 模型类做些调整:让 add_product()方法返回与新加货品对应的 CartItem 对象。图 9.2 加上黄渐变效果的购物车depot_m/app/models/cart.rbdef add_product(product)current_item = items.find |item| item.product = productif current_itemcurrent_item.increment_quantityelse current_item = CartItem.new(product) ite

17、ms . 经过这三个小修改之后,最近发生变化的货品所对应的元素就会被打上id=“current_item“的标记。现在,我们只要告诉 JavaScript 对这些元素施加高亮效果就行了:在add_to_cart.js.rjs 模板中调用 visual_effect 方法。depot_m/app/views/store/add_to_cart.js.rjspage.replace_html(“cart“ , :partial = “cart“ , :object = cart)page:current_item.visual_effect :highlight,:startcolor = “#8

18、8ff88“ ,:endcolor = “#114411“请注意我们是如何指定“想要施加可视化效果的元素”的:只要把:current_item 传递给 page就行了。然后我们对这些元素施加了高亮显示的可视化效果,并将默认的“黄-白”色改成了更适合我们设计风格的色调。现在点击按钮往购物车里添加一件货品,你就会看到新添加的货品会显示为亮绿色,然后逐渐变淡,最后完全融入背景。9.4 迭代 D4 :隐藏空购物车Iteration D4: Hiding an Empty Cart来自客户的最后一个请求:即便购物车中没有任何东西,现在我们还是把它显示在边框里;能不能只在其中有东西的时候才显示?当然可以!

19、实际上,可选的办法有好几种。最简单的做法大概是:只有当购物车中有东西时才包含显示它的那段 HTML 代码。这个方案只需要修改_cart 一个局部模板就可以实现。Your Cart“cart_item“ , :collection = cart.items) %Total:empty_cart %虽然这也是可行的方案,但却让用户界面显得有点生硬:当购物车由空转为不空时,整个边框都需要重绘。所以,我们不要这样做,还是把它做更圆滑一点吧。script.aculo.us 提供了几个可视化效果,可以漂亮地让页面元素出现在浏览器上。我们现在来试试 blind_down,它会让购物车平滑地出现,并让边框上其

20、他的部分依次向下移动。毫不意外地,我们会在现有的.js.rjs 模板中调用这个效果。因为 add_to_cart 模板只有在用户向购物车中添加货品时才会被调用,所以我们知道:如果购物车中有了一件货品,那么就应该在边框中显示购物车了(因为这就意味着此前购物车是空的,因此也是隐藏起来的)。此外,我们需要先把购物车显示出来,然后才能使用背景高亮的可视化效果,所以“显示购物车”的代码应该在“触发高亮效果”的代码之前。现在,这个模板大概就是这样:.depot_n/app/views/store/add_to_cart.js.rjspage.replace_html(“cart“ , :partial =

21、 “cart“ , :object = cart)page:cart.visual_effect :blind_down if cart.total_items = 1page:current_item.visual_effect :highlight,:startcolor = “#88ff88“ ,:endcolor = “#114411“它暂时还不能工作,因为 Cart 模型类中还没有 total_items()这个方法。depot_n/app/models/cart.rbdef total_itemsitems.sum |item| item.quantity end我们还需要在购物车

22、为空的时候将其隐藏起来。有两种基本的办法可以做这件事。第一,就像本节开始处展示的,根本不生成任何 HTML 就好了。遗憾的是,如果我们这样做,当用户朝空的购物车中放入第一件货品时,浏览器上就会出现一阵闪烁购物车被显示出来,然后又隐藏起来,然后再被 blind_down 效果逐渐显示出来。所以更好的解决办法是创建“显示购物车”的 HTML,但对它的 CSS 样式进行设置,使其不被显示出来如果购物车为空,就设置为 display:none。为此,我们需要修改 app/views/layouts 目录下的 store.html.erb 布局文件。首先想到的修改方法大致如下:style=“displa

23、y: none“cart“ , :object = cart) %当购物车为空时,这段代码就会给标记加上 style=“display: none“这段 CSS 属性。这确实有效,不过真的、真的很丑陋。下面挂着一个孤孤单单的“”字符,看起来就像放错了地方一样(虽然确实没放错);而且更糟糕的是,逻辑被放到了 HTML 标记的中间,这正是给模板语言带来恶名的行为。不要让这么丑陋的代码出现,我们还是创建一层抽象编写一个辅助方法把它隐藏起来吧。辅助方法Helper Methods每当需要将某些处理逻辑从视图(不管是哪种视图)中抽象出来时,我们就应该编写一个辅助方法。进入 app 目录,你会看到下列子目

24、录:depot ls p appcontrollers/ helpers/ models/ views/毫不意外地,我们的辅助方法应该放在 helpers 目录下。进入这个目录,你会看到其中已经有几个文件存在了。depot ls p app/helpersapplication_helper.rb products_helper.rb store_helper.rbRails 代码生成器会自动为每个控制器(在我们这里就是 products 和 store)创建一个辅助方法文件;Rails 命令本身(也就是一开始创建整个应用程序的命令)则生成了 application_helper.rb文件。如

25、果你愿意,可以把方法放到某个控制器对应的辅助文件中,但实际上视图可以使用所有的辅助方法。目前还只有在 store 控制器的视图中需要使用它,所以就先把它放在 store_helper.rb 文件中吧。我们先来看看 store_helper.rb 这个文件,它就位于 helpers 目录下:module StoreHelperend.我们来编写一个名叫 hidden_div_if()的辅助方法,它接收三个参数:一个条件判断, 一组可选的属性以及一个代码块。该方法将代码块产生的结果用标记包装起来,如果条件判断为 true,就给它加上 display:none 样式。在布局文件中,我们会这样使用它:

26、depot_n/app/views/layouts/store.html.erb“cart“ ) do %“cart“ , :object = cart) %我们把这个辅助方法放在 app/helpers 目录下的 store_helper.rb 文件中。depot_n/app/helpers/store_helper.rbmodule StoreHelperdef hidden_div_if(condition, attributes = , Pragmatic ProjectAutomationnnn nform method=“post“action=“/store/empty_cart

27、“ class=“button-to.显然事情不应该这样。即便用户的浏览器禁用了 JavaScript,我们的应用程序也应该能够工作。这就是我们下一个迭代的任务。9.5 迭代 D5 : JavaScript 被禁用时的对策Iteration D5: Degrading If Javascript Is Disabled.在第 117 页,我们把购物车搬到了边栏上来显示,此时应用程序里还没有任何一行 Ajax 代码。如果在 JavaScript 被浏览器禁用时能够回到此时的行为,那么我们的应用程序就同样能够满足 Bruce 的需要了。换句话说,如果针对 add_to_cart 的请求不是来自 J

28、avaScript,我们就希望应用程序仍然采取原来的行为,并将浏览器重定向到首页。当首页显示出来时,更新之后的购物车就会出现在边栏上了。当用户点击 form_remote_tag 中的按钮时,可能会出现两种不同的情况。如果 JavaScript 被禁用,表单的目标 action 就会被一个普通的 HTFP POST 请求直接调用这就是一个普通的表单;如果允许使用 JavaScript,它就不会发起普通的 POST 调用,而由一个 JavaScript 对象和服务器建立后台连接一这个 JavaScript 对象是 XmlHTTPRequest 的实例,由于这个类的名字颇有点拗口,很多人(以及 R

29、ails)把它简称为 xhr。所以,我们可以在服务器上检查进入的请求是否由 xhr 对象发起的,从而判断浏览器是否禁用了JavaScript。Rails 提供的 request 对象在控制器和视图中都可以访问这个对象让你可以很方便地做这个检查:调用 xhr?方法就行了。这样一来,只要在 add_to_cart 中加上两行代码,不管用户的浏览器是否允许使用 JavaScript,我们的应用程序就都能支持了。Download depot_o/app/controllers/store_controller.rbdef add_to_cartproduct = Product.find(params

30、:id)cart = find_cartcurrent_item = cart.add_product(product)respond_to do |format| format.js if request.xhr? format.html redirect_to_indexendrescue ActiveRecord:RecordNotFoundlogger.error(“Attempt to access invalid product #params:id“ )redirect_to_index(“Invalid product“ )end9.6 我们做了什么What We Just D

31、id在这个迭代中,我们给购物车加上了 Ajax 的支持。 我们把购物车搬到了边栏里,并且让 add_to_cart 这个 action 重新显示货品分类页面。 我们用 form_remote_tag()来调用 add_to_cart,向它发起 Ajax 请求。 我们利用 RJS 模板,单独更新购物车那一小块的 HTML。 为了让用户更加清晰地看到购物车的变化,我们加上了高亮显示的效果仍然是用 RJS 模板实现的。 我们编写了一个辅助方法,当购物车为空时将其隐藏起来,添加货品时再用 RJS 模板将其显示出来。 最后,我们让应用程序在禁用了 JavaScript 的浏览器上也能工作此时它将采取没有

32、加入 Ajax之前的行为方式。.请牢记我们所采用的渐进式的 Ajax 开发方式:首先从一个传统的应用程序开始,逐渐向其中增加Ajax 的特性。Ajax 可能很难调试;如果逐渐添加 Ajax 特性,当遇到困难时,你也可以比较容易地找出问题所在。另外,正如我们看到的,从一个传统的应用程序开始也使你能够更容易地同时支持 Ajax 和非 Ajax 的行为方式。最后还有两件事值得一提。首先,如果你打算进行大量的 Ajax 开发,也许你应该花点时间去熟悉浏览器上的 JavaScript 调试工具和 DOM 监视工具。Pragmatic Ajax:A Web 2.0 PrimerGGA06一书的第 8 章在

33、这方面提供了很多有用的提示。其次,Firefox 上的 NoScript 插件可以轻而易举的改变JavaScript 运行 /禁止状态,另外有人发现在开发过程中同时运行两个不同的浏览器会很有帮助:一个浏览器允许使用 JavaScript,另一个禁用。当添加新特性时,分别用这两个浏览器来检查,以确保不管是否允许使用 JavaScript 都能使用这些功能。游戏时间Playtime下面的东西,不妨自己动手试试。 在 7.4 节游戏时间里有一项内容是通过点击图书封面图像来将图书加入到购物车中,现在把它改为使用 form_remote_tag 和 image_submit_tag。 当用户清空购物车时

34、,我们就将购物车隐藏起来。目前我们实现这一功能的方式是重绘整个货品列表页面。你能否修改应用程序,改用 Script aculo.us 提供的 blind_up 效果来实现隐藏购物车? 如果浏览器禁用了 JavaScript,你刚才所做的修改还能工作吗? 尝试对新放入购物车的货品使用别的可视化效果。譬如说,能否将其初始状态设为隐藏,然后逐渐出现?这是否会影响在 Ajax 代码和初始页面显示之间共享“购物车条目”的局部模板? 给购物车中的每个条目添加一个链接,点击该链接就会将当前条目的数量减 1,如果数量减到 0,则删除该条目。请首先用非 Ajax 的方式实现这一功能,然后加上 Ajax 效果。(更多提示请看 http:/

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 教案示例

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com