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.
Cậu ơi giúp tớ mục này với User model// def add_to_list(item) current_item = enrollments.find_by(item_id: item.id) if current_item current_item.quantity += 1 else current_item = enrollments.build(item_id: item.id) end current_item end def remove_from_list(item) current_item = enrollments.find_by(item_id: item.id) if current_item && current_item.quantity > 2 current_item.quantity -= 1 else current_item.destroy end current_item end Phần add_to_list nó chạy rất ok nhưng khi mình muốn sử dụng phần xóa 1 items đi thì nó lại không chạy .destroy thì chạy nhưng current_item.quantity -= 1 thì không. Rõ ràng là code có chạy 1 phần nhưng mục giảm số lượng thì lại không chạy đc :(( Còn đây… Đọc thêm »
current_item.destroy chạy nhưng current_item.quantity -= 1 không chạy là đúng mà bạn.
Chỗ đó bạn làm if…else, một cái chạy thì cái kia phải không chạy chứ 🙂
cảm ơn cậu, còn 1 cái cực kì quan trọng nữa
many to many association mà qua through: :cart ý
Làm sao để edit thông tin ở đó vậy nhỉ
User >> CART << Item
t muốn tạo 1 cái form để update
User.find(58).CART.first.quantity
Làm cách nào để tớ update or Put cái quantity của Item add vào trong giỏ hàng đc nhỉ
sr cậu nếu t giải thích hơi khó hiểu.
Túm lại là update trong Many to Many association
Ý bạn là update đối tượng Item thông qua đối tượng Cart phải không?
Nếu vậy thì bạn chỉ cần lấy đối tượng Item đó là được:
ý tớ là giống như là update item ý, sách này nó chỉ dạy mình cách add thêm vào thôi chứ không chỉ cách chọn item bất kì giống như các trang TMĐT khác, tớ thử làm 2 cách là 1. tạo edit cho phần item trong cart 2. get @quantity = params[:quantity] Đều k được Đành phải dùng cách cuối là 2 lựa chọn add more 1 và 10 Cậu nghĩ ra cách nào mà update được dựa trên Many to Many association thì chỉ tớ nhé, làm kiểu bình thường không ăn thua Tớ chèn item vào mục… Đọc thêm »
À mình hiểu ý bạn rồi, hiện tại của mình chỉ có một chức năng duy nhất là xóa sạch cái cart chứ không thêm/bớt từng sản phẩm được.
Có điều mình cũng chưa hiểu cái Many to Many thì có liên quan gì? Làm kiểu bình thường là làm như thế nào?
Mình nghĩ ở đây chỉ có 2 thao tác cần làm là thay đổi
quantity
và xóa sản phẩm thôi. Mấy cái này thì bạn chỉ cần tạo phương thức action mới, định nghĩa routing cho nó rồi gọi AJAX hay HTTP gì cũng được.Có cậu ạ ví dụ nhé
K có association: Tạo user mới thì là User.create
User has many item and (Item belongs_to a User) @item = current_user.items.build
Còn với mục has many, :through
User has many items :through :line_item
Item has many users :through :line_item
line_item belongs_to ..
Model LineItem gồm có user_id. item_id, quantity
Thì tớ không biết để update quantity của LineItem bằng form dùng như thế nào
Ok, mình làm ví dụ cho bạn này, bạn xem có giải quyết được không nhé: Đầu tiên vào file app/views/carts/_cart.html.erb thêm dòng này: <%= button_to 'Update cart', edit_cart_path(current_cart), :method => :get %> Dòng này nó tạo nút bấm dẫn đến trang /carts/<id>/edit?. Rails nó tạo sẵn cho mình cái trang /carts/<id>/edit? dựng từ file app/views/carts/edit.html.erb nên mình tận dụng luôn, đầu tiên bạn vào file đó sửa lại như này: <h2>Your cart</h2> <tabel> <% @cart.line_items.each do |line_item| %> <%= form_for(line_item) do |f| %> <tr> <td> <%= f.text_field :quantity, :size => '1' %> x <%= f.label line_item.product.title %> </td> </tr>… Đọc thêm »
T làm được r, chắc t phải tiết kiệm đc 3 ngày nhờ cm này của cậu :)). Cần gì mình trao đổi thêm nhé.
Ok bạn!