读书人

Agile Web Development with Rails 4E

发布时间: 2012-10-17 10:25:46 作者: rapoo

Agile Web Development with Rails 4E 学习笔记3

Chapter 11. Task F. Add a Dash of Ajax
6.1 使用partial重构Cart:
??? partial(局部)模板简称:partials.可以在其他的模板或Controller中调用(呈现)这个局部模板.
??? 那么在调用的地方,局部模板就可以呈现出数据.
??? 你也可以传递参数给局部模板就像传递参数给方法一样来得到不同的呈现结果(传递了Collection).
???
??? The partial template itself is simply another template file (by default in the same directory
??? as the object being rendered and with the name of the table as the name).
??? 局部模板默认所在的视图目录是 和模板里要呈现的对象(line_item)的相同目录下(views/line_items/).
??? 局部模板的文件名默认是render括号里的(最终)参数,如果是对象关联,则采用单数形式.???
???
??? Inside the partial template, we refer to the current object using the variable name that
??? matches the name of the template.? _line_item.html.erb对应的模板为line_item(:去掉文件名头的_)
??? 所以在局部模板里,使用和模板名字相同的变量即line_item.

-> app/views/carts/show.html.erb  <% @cart.line_items.each do |item| %><tr><td><%= item.quantity %>×</td><td><%= item.product.title %></td><td name="code">-> app/views/carts/show.html.erb<%= render @cart %>views/carts/show.html.erb只有一行代码. render跟上的参数是实例变量@cart-> app/views/carts/_cart.html.erb<div name="code">-> app/views/layouts/application.html.erb<div id="cart"><%= render @cart %></div>  -> app/controllers/store_controller.rb  class StoreController < ApplicationController  def index@products = Product.all@cart = current_cart  endend-> app/controllers/line_items_controller.rbif @line_item.save#format.html{ redirect_to @line_item.cart}format.html { redirect_to(store_url) }

??? When we click to add an item to the cart, the page is redisplayed with an updated cart.
?? ?重定向到主页面是整个页面都刷新,而变化的只是partial中的内容.其他内容一般是不会变化的.Ajax Coming!
?? ?
6.4 购买按钮发送Ajax请求,应用程序使用RJS模板呈现内容:?? ?
?? ?change the catalog page to send an Ajax to our server application
?? ?button_to:create the link to the create action.加上:remote参数表示现在发送的是一个Ajax请求.
?? ?改变页面向服务器端应用程序发起一个Ajax请求,
?? ?
?? ?have the application respond with the HTML fragment containing the updated cart.
?? ?create the updated HTML fragment that represents the cart
?? ?to have the browser stick that HTML into the browser’s internal representation of DOM being displayed.
?? ?应用程序响应,并应答一段HTML代码(展示了最新的购物车).
?? ?创建一段HTML代码来代表购物,然后让浏览器把这段代码插入到当前页面的DOM,替换掉当前页面显示的购物车.
?? ?
?? ?when create finishes handling the Ajax request, Rails will look for a create template to render
?? ?当在create方法中处理完Ajax请求(点击购买,调用create方法,line_item.save成功)
?? ?根据format.js,Rails会去寻找对应的模板来呈现内容:create.js.rjs.
?? ?正如format.html(后面没有加上redirect_to).默认对应的是方法名.文件后缀.比如new方法:format.html->new.html.erb
?? ?
?? ?A .js.rjs template is a way of getting JavaScript on the browser to do what you want,
?? ?all by writing server-side Ruby code. rjs模板允许你能够在浏览器上得到JS代码即:将JavaScript发送到浏览器.

-> app/views/store/index.html.erb<%= button_to 'Add to Cart', line_items_path(:product_id => product), :remote => true %>-> app/controllers/line_items_controller.rbdef createrespond_to do |format|if @line_item.saveformat.html { redirect_to(store_url) }format.jsendendend-> app/views/line_items/create.js.rjspage.replace_html('cart', render(@cart))

??? The page variable is an instance of JavaScript generator-a Rails class that knows
?? ?how to create JavaScript on the server and have it executed by the browser.
?? ?Rails提供的JavaScript(帮助类,application Layout中包含库:<%= javascript_include_tag :defaults %>)
?? ?生成器类的实例变量page,能够在服务器端创建JS,并使其在浏览器上运行.

?? ?replace the content of the element on the current page with the id cart with the rendered partial for a given cart.
?? ?This simple RJS template then tells the browser to replace the content of the element whose id="cart" with that HTML.
?? ?将当前页面id为cart的元素替换成render @cart的内容(局部模板里的内容就是最新的购物车的内容).

?? ?调试Ajax程序:
?? ?查看development.log是否有报错;或者是否有请求调用到create方法.若没有说明浏览器没有发起Ajax请求.
?? ?查看页面源文件检查JS库是否加载到浏览器;?? 浏览器缓存;? 针对IE的头信息:<!DOCTYPE html>
?? ?
6.5 高亮显示最近更新的购物项:
?? ?identifying the most recently updated item in the cart.
?? ?pass the current line item down to the template(create.js.rjs) by assigning it to an instance variable
?? ?标识购物车中最近更新的购物项. Ajax请求处理完后(在format.js后添加)将当前购物项作为实例变量返回.
?? ?
?? ?In the _line_item.html.erb partial, we then check to see whether the item we’re rendering is
?? ?the one that just changed. If so, we tag it with an id of current_item:
?? ?在购物项局部模板中(因为最终显示的购物项内容其实是在_line_item局部模板里)而不是在application或者carts/show中.
?? ?在Controller的Action#create(添加完购物项保存成功)设置的实例变量,在局部模板里仍然是可以访问到的@current_item.
?? ?在_cart局部模板里render cart.line_items循环购物的每个购物项,如果当前循环到的购物项=实例变量里保存的变量
?? ?@current_item 则把这个购物项标记一个id:<tr id="current_item">.最后只要把黄渐变的效果运用到这个元素上即可.
?? ?
?? ?now the <tr> element of the most recently changed item in the cart will be tagged with id="current_item".?? ?
?? ?identified the browser element that apply the effect to by passing :current_item to the page.

-> app/controllers/line_items_controller.rbdef createif @line_item.saveformat.html { redirect_to(store_url) }format.js { @current_item = @line_item }-> app/views/line_items/_line_item.html.erb   <% if line_item == @current_item %><tr id="current_item">   <% else %><tr>   <% end %><td><%= line_item.quantity %>×</td><td><%= line_item.product.title %></td><td style="display:none">不显示购物车.

?

-> app/views/line_items/create.js.rjs  #采用Ajax将最近更新的购物项替换原来的购物车page.replace_html('cart', render(@cart))   #如果购物车中有了一项购物项,则在边框就应该显示购物车.page[:cart].visual_effect :blind_down if @cart.total_items == 1  #高亮显示最近更新的购物项page[:current_item].visual_effect :highlight, :startcolor => "#88ff88", :endcolor => "#114411"-> app/models/cart.rb#6种写法: 成员变量前@  数组.to_a  用Symbol:代替临时变量def total_priceline_items.to_a.sum {|item| item.total_price}#@line_items.to_a.sum {|item| item.total_price}#@line_items.sum {|item| item.total_price}#line_items.sum {|item| item.total_price}#@line_items.sum(:total_price)#line_items.sum(:total_price)enddef total_items #购物车所有购物项的所有数量.比如购物项1 数量2 购物项2 数量3 则只为5line_items.sum(:quantity)#line_items.sum{ |item| item.quantity }end-> app/views/layouts/application.html.erb<div id="cart" <% if @cart.line_items.empty? %>style="display:none"<% end %> ><%= render(@cart) %></div>

??? using Ajax to add products to the cart, the main page doesn’t get redrawn
?? ?between requests as people shop.? we’ll continue to display the flash message
?? ?saying the cart is empty even as we display a cart in the sidebar.
?? ?清空购物车时,不需要显示flash消息. 否则如果再次往购物车中添加Product,因为采用了Ajax
?? ?只刷新了局部模板,没有刷新整个页面,导致flash消息仍然停留在主页面上,造成困惑.

-> app/controllers/carts_controller.rbdef destroy#format.html { redirect_to(store_url, :notice => 'Your cart is currently empty') }format.html { redirect_to(store_url) }

?? abstract some processing out of a view (any kind of view),write a helper method.
?? ?当需要将处理逻辑从视图(partial,rjs模板)中抽象出来,可以编写辅助方法.
?? ?
6.7 抽象视图中的处理逻辑:
?? ?It take a condition, an optional set of attributes, and a block.
?? ?if the condition is true,adding the display: none style.
?? ?It wraps the output(content_tag) generated by the block in a <div> tag. [wraps output]
?? ?把(传递给它:content_tag的)代码块的输出用标记(div)包装起来.如条件为真,则(div)加上style样式
?? ?
?? ?By using the &block notation, we get Ruby to pass the block that was given to hidden_div_if
?? ?down to content_tag.? [pass block down to content_tag]
?? ?通过&block标志,将代码块经过hidden_div_if()传递给content_tag(). 有点类似回调函数.参数继续传递.

-> app/helpers/application_helper.rbmodule ApplicationHelperdef hidden_div_if(condition, attributes = {}, &block)if conditionattributes["style"] = "display: none"endcontent_tag("div", attributes, &block)endend-> app/views/layouts/application.html.erb<%= hidden_div_if(@cart.line_items.empty?, :id => "cart") do %><%= render @cart %><% end %>

??? make the cart hide and reveal itself
?? ?by making the CSS display style conditional on the number of items in the cart.? ?
?? ?used an RJS template to invoke the blind_down effect when the cart went from being empty to having one item.
?? ?显示和隐藏购物车: 根据Cart中购物项的数量设置CSS样式;当第一件Product被放进购物车时,通过RJS模板调用效果.
?? ?
6.8 测试采用Ajax引起的问题:
?? ?> rake test
?? ?或者访问http://localhost:3000/products报错:NoMethodError in Products#index
?? ?where line #17 raised: <%= hidden_div_if(@cart.line_items.empty?, :id => "cart") do %>
?? ?
?? ?如果把store_controller.rb的@cart = current_cart注释掉.同样报错:NoMethodError in Store#index
?? ?where line #17 raised:<%= hidden_div_if(@cart.line_items.empty?, :id => "cart") do %>
?? ?这就是我们前面说到的.如果Controller中没有定义变量@cart,在视图中使用就无法使用相应的变量(6.3).

?? ?@cart is apparently nil when we display an index of our products.it is set only in the store controller.
?? ?avoid displaying the cart at all unless this value is set:

-> app/views/layouts/application.html.erb<% if @cart %><%= hidden_div_if(@cart.line_items.empty?, :id => "cart") do %><%= render @cart %><% end %><% end %>-> test/functional/line_items_controller_test.rbtest "should create line_item" doassert_difference('LineItem.count') do  #post :create, line_item: @line_item.attributes  post :create, :product_id => products(:ruby).idend#assert_redirected_to line_item_path(assigns(:line_item))#assert_redirected_to cart_path(assigns(:line_item).cart)assert_redirected_to store_pathend  test "should create line_item via ajax" doassert_difference('LineItem.count') doxhr :post, :create, :product_id => products(:ruby).idend#Instead of a redirect,we expect a successful responseassert_response :success#containing a call to replace the HTML for the cart#extract the relevant HTML and then processing that HTML via whatever additional assertions you want to applyassert_select_rjs :replace_html, 'cart' do#find a row with an id of current_item with a value matching Programming Ruby 1.9assert_select 'tr#current_item td', /Programming Ruby 1.9/endend
?

?

读书人网 >Web前端

热点推荐