Agile Web Development with Rails 3nd Edition學習筆記-使用Ajax將購物車放入Sidebar

之前,當用戶點擊了“Add to Cart“按鈕之後,頁面會跳轉到add_to_cart頁面。用戶想繼續選購產品需要點擊瀏覽器的回退按鈕才能再回到產品列表頁面。另外,用戶在查看產品列表頁面是如果想看看自己已經選了些什麼東西,以及需要花多少錢時,在不添加新的產品到購物車的情況下,其實是做不到的。這個用戶帶來了很大的不便。
所以,我們想改進我們的網站。把購物車的列表擺放到頁面的Sidebar上。這樣用戶不論在什麼時候都可以查看自己已經選了些什麼,以及要花多少錢。
那麼,我們開始吧。
要做到這些,我們需要一些Ajax的技術。Rails提供了一個叫partial的模板來實現它。
首先,我們來分析以下:
1、我們要在Sidebar上顯示購物車的信息,那麼我們需要在depot/app/views/layouts/store.html.erb文件中添加相應的代碼。而數據則是從cart對象中取得的。
2、當我們點擊“Add to Cart”按鈕時,觸發add_to_cart方法,把一個CartItem對象添加到Cart對象中,並要顯示add_to_cart.html.erb文件中的內容。
也就是說,在使用add_to_cart.html.erb文件顯示購物車信息時,頁面和數據之間的關係是:
[quote]add_to_cart.html.erb
+-- Cart
+-- CartItem[/quote]
同樣的,如果我們用store.html.erb中的Sidebar來顯示購物車中的信息時,頁面和數據間的關係是:
[quote]store.html.erb
+-- Cart
+-- CartItem[/quote]
那麼,我們需要爲Cart和CartItem創建partial,並在store.html.erb中使用Cart的partial,而在Cart的partial中使用CartItem的partial。
好的,依賴關係搞清楚了之後。我們先來說一下partial的一些東西。
首先,我的理解是,partial實際上和C#中的partial類很相似。C#中的partial類實際上是在多個文件中定義本應在一個文件中定義的類。編譯時這些文件中的定義將被合併到這個類的定義中。
那麼Rails中的partial實際上是在多個文件中定義一個頁面,而實際使用中,這些信息將被合併。
在使用一個partial時,使用類似下面的代碼:
當將被引用的partial實際使用一個對象實例時
<%= render(:partial => "partial-name" , :object => @obj-name) %>

當將被引用的partial實際使用的是一個對象的集合時
<%= render(:partial => "partial-name" , :collection => collection-obj) %>

這裏,partial-name一般使用和對應對象相同的名稱,但也可以不同。但是要注意的是,傳給下一級partial時,包含數據的對象的名稱將和partial-name相同。
還有一點,與這個partial-name對應的partial文件的文件名,根據Rails的約定,必須按照下面格式命名:
[quote]_partial-name.html.erb[/quote]

好了,現在我們來實現我們前面描述的功能吧。
根據上面頁面和對象的依賴關係,首先,我們要在store.html.erb加入對Cart的partial引用:
在該文件id爲“side”的div的開始部分加入如下代碼:
<div id="cart">
<%= render(:partial => "cart" , :object => @cart) %>
</div>

這裏,我們定義了一個叫“cart”的partial。根據上面提到的命名規則,對應的partial文件名爲:_cart.html.erb。
接下來,我們來定義這個Cart的partial。
<div class="cart-title">Your Cart</div>
<table>
<%= render(:partial => "cart_item" , :collection => cart.items) %>
<tr class="total-line">
<td colspan="2">Total</td>
<td class="total-cell"><%= number_to_currency(cart.total_price) %></td>
</tr>
</table>
<%= button_to "Empty cart" , :action => :empty_cart %>

在這個文件中,render所在的行其實替換掉了原先的add_to_cart.html.erb文件中循環顯示每個CartItem的部分,其它部分沒有改變。我的理解是,“:collection”實際上會爲後面指定的cart.items中的每一個對象調用由“:partial”指定的partial文件定義的處理,從而產生和之前循環顯示一樣的效果。
那麼,我們接下來定義_cart_item.html.erb
<tr>
<td><%=h cart_item.title %></td>
<td>×<%= cart_item.quantity %></td>
<td class="item-price"><%= number_to_currency(cart_item.price) %></td>
</tr>

這個可以很明顯的看出,這個partial中的定義實際上是原先add_to_cart.html.erb中被循環使用的部分。
從這兩個partial我們可以看出,他們實際上是把原先add_to_cart.html.erb中定義的內容拆分到了兩個文件中定義。而這兩個文件通過Rails提供的機制在調用是被整合到了一起。

這樣,最主要的部分我們就做完了。爲了不讓用戶在點擊“Add to Cart”按鈕時再顯示add_to_cart頁面,我們需要對store_controller.rb文件做一些修改。
首先,我們要讓redirect_to_index方法可以無參調用,以便我們在不需要消息的時候跳轉到index頁面。修改後的代碼如下:
def redirect_to_index(msg = nil)   <== Changed
flash[:notice] = msg if msg <== Changed
redirect_to :action => 'index'
end

接下來,我們不希望add_to_cart方法在調用完之後顯示add_to_cart頁面,因此,我們在添加完產品之後讓頁面直接跳轉回index頁面。修改後的代碼如下:
def add_to_cart
product = Product.find(params[:id])
@cart = find_cart
@cart.add_product(product)
redirect_to_index <== New
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid product #{params[:id]}")
redirect_to_index("Invalid product")
end


好了,功能實現部分就好了。其實這個時候add_to_cart.html.erb文件就沒有用了。因爲add_to_cart方法在完成時已經指向index頁面了。

爲了讓我們的頁面看起來美觀些,在depot.css文件中加入如下式樣信息:
#ca rt, #ca rt table {
font-size: smaller;
color: white;
}
#ca rt table {
border-top: 1px dotted #595 ;
border-bottom: 1px dotted #595 ;
margin-bottom: 10px;
}


這樣我們的目標就達成了。現在可以打開服務,嘗試以下我們修改後的效果了。
下面是我做好的頁面圖,給大家看看效果:
[img]http://dl.iteye.com/upload/attachment/240543/fd9fde5a-d9a3-314f-ab72-1ca168eeab80.jpg[/img]

[color=orange]注:這篇筆記加入了很多我自己的分析部分。由於是第一次使用這樣的技術,理解還很淺薄,如果哪裏有什麼不正確的地方,還希望知道的朋友能給指正出來。謝謝![/color]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章