Chúng ta sẽ thêm nút xóa giỏ hàng, hiển thị tổng tiền cho giỏ hàng.
Thêm nút xóa giỏ hàng
Đầu tiên chúng ta sửa lại file View show.html.erb
trong thư mục app/views/carts
như sau:
<%= notice %> <h2>Your cart</h2> <ul> <% @cart.line_items.each do |a| do %> <li><%= a.quantity %> x <%= a.product.title %></li> <% end %> </ul> <%= button_to 'Empty cart', @cart, :method => :delete, data: {:confirm => 'Are you sure?'} %>
Chúng ta gọi phương thức button_to
để tạo một nút bấm với nhãn là ‘Empty cart’, URL chúng ta lấy từ biến @cart,
nếu muốn bạn vẫn có thể dùng các phương thức như cart_path(),
phương thức gửi lên là DELETE,
tham số :data
với thuộc tính :confirm
sẽ tạo ra một hộp thoại như hàm alert()
trong Javascript nếu bạn còn nhớ.
Ngoài ra chúng ta cũng đã xóa 2 nút Edit và Back do Rails tự tạo nếu bạn còn nhớ:
<%= link_to 'Edit', edit_cart_path(@cart) %> | <%= link_to 'Back', carts_path %>
Nút ‘Empty cart’ sẽ gọi đến phương thức destroy
trong lớp controller CartsController,
trong trang /cart
Rails cũng đã tạo cho chúng ta một nút như vậy với nhãn là ‘Destroy’ (nếu muốn bạn có thể vào xem thử), và mặc định phương thức destroy
trong lớp Controller sẽ xóa giỏ hàng với id được truyền từ tham số, tuy nhiên ở đây chúng ta muốn phương thức này chỉ xóa giỏ hàng nào đang được lưu trong session
của trình duyệt thôi, do đó chúng ta sửa lại phương thức destroy
này như sau:
class CartsController < ApplicationController . . . # DELETE /carts/1 # DELETE /carts/1.json def destroy @cart = current_cart @cart.destroy session[:cart_id] = nil respond_to do |format| format.html { redirect_to carts_url, notice: 'Cart was successfully destroyed.' } format.json { head :no_content } end end . . . end
Chúng ta lấy model Cart
hiện tại bằng phương thức current_cart
đã được định nghĩa trong lớp ApplicationController,
sau đó gọi hàm destroy
để xóa, cuối cùng gán giá trị trong session
là nil
.
Hiển thị tổng số tiền trong giỏ hàng
Chúng ta sửa lại file View show.html.erb
một lần nữa như sau:
<%= notice %> <h2>Your cart</h2> <table> <% @cart.line_items.each do |item| %> <tr> <td><%= item.quantity %> x </td> <td><%= item.product.title %></td> <td class="item_price"><%= number_to_currency(item.total_price) %></td> </tr> <% end %> <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', @cart, :method => :delete, data: {:confirm => 'Are you sure?'} %>
Ở đây thay vì dùng các thẻ <ul>, <li>
để hiển thị danh sách thì chúng ta sẽ dùng bảng. Giá trị của tổng số tiền trên từng LineItem
và tổng số tiền của Cart
sẽ lấy từ phương thức total_price
của lớp model tương ứng.
Các thẻ trên cũng dùng một số lớp CSS riêng, chúng ta định nghĩa các lớp CSS mới như sau:
. . . #store .cart_title { font: 120% bold; } #store .item_price, #store .total_line { text-align: right; } #store .total_line .total_cell { font-weight: bold; border-top: 1px solid #595; }
Và bây giờ chúng ta sẽ định nghĩa phương thức total_price
cho từng model, đầu tiên chúng ta định nghĩa cho lớp LineItem
như sau:
class LineItem < ActiveRecord::Base belongs_to :product belongs_to :cart def total_price product.price * quantity end end
Rất đơn giản, số tiền bằng tổng số sản phẩm nhân với đơn giá sản phẩm.
Tiếp theo là lớp Cart:
class Cart < ActiveRecord::Base has_many :line_items, :dependent => :destroy def add_product(product_id) current_item = line_items.find_by_product_id(product_id) if current_item current_item.quantity += 1 else current_item = line_items.build(:product_id => product_id) end current_item end def total_price line_items.to_a.sum { |item| item.total_price } end end
Ở đây số tiền sẽ bằng tổng số tiền của các đối tượng LineItem
cộng lại. Phương thức to_a
sẽ chuyển một đối tượng danh sách sang kiểu mảng, sau đó chúng ta gọi phương thức sum
của mảng đó.
Bạn có thể xem tổng tiền và có thể xóa giỏ hàng được rồi:
Ngoài ra nếu trước khi làm chức năng session mà bạn đã từng tạo nhiều giỏ hàng thì trong trang /carts
(liệt kê danh sách giỏ hàng), trang này gọi đến phương thức index
trong lớp CartsController,
chúng ta sẽ thấy có nhiều dòng giỏ hàng, đây là trang hiển thị mặc định của Rails mà đến bây giờ chúng ta vẫn chưa chỉnh sửa gì trong này.
Trang này có hiển thị nút ‘Destroy’ để bạn xóa giỏ hàng, tuy nhiên sau khi làm chức năng chỉ xóa giỏ hàng hiện tại ở trên thì bạn chỉ có thể xóa một giỏ hàng trong danh sách đó thôi, mặc dù nếu muốn bạn vẫn có thể bấm nút xóa trên các giỏ hàng khác và Rails vẫn thông báo xóa thành công, nhưng khi load lại trang thì bạn vẫn thấy các giỏ hàng đó tồn tại. Chúng ta sửa lại để hàm này chỉ hiển thị giỏ hàng trong session của trình duyệt như sau:
class CartsController < ApplicationController . . . # GET /carts # GET /carts.json def index @carts = [] if session[:cart_id] != nil @carts << Cart.find_by_id(session[:cart_id]) end end . . . end
Vậy là xong, bây giờ nếu bạn mở 2 trình duyệt hoặc mở 2 máy khác nhau, và mỗi trình duyệt đều có giỏ hàng riêng, và cùng trỏ đến trang /carts
thì bạn chỉ thấy giỏ hàng của từng trình duyệt đó chứ không thấy giỏ hàng của trình duyệt khác.