Trong các bài trước chúng ta đã xây dựng cơ chế session và tạo mối quan hệ giữa Product và Cart,
bây giờ chúng ta sẽ xây dựng chức năng tạo giỏ hàng.
Đầu tiên chúng ta sửa lại file index.html.erb
trong thư mục app/views/store
như sau:
<% if notice %> <p id="notice"><%= notice %></p> <% end %> <h1>Product List</h1> <% @products.each do |product| %> <div class="entry"> <%= image_tag(product.image_url) %> <h3><%= product.title %></h3> <%= sanitize(product.description) %> <div class="price_line"> <span class="price"><%= number_to_currency(product.price) %></span> <%= button_to 'Add to Cart', line_items_path(:product_id => product) %> </div> </div> <% end %>
Phương thức button_to
sẽ tạo một thẻ <form></form>
với class là button_to,
bên trong có một thẻ <input>
với thuộc tính type
là Submit,
nhãn là tham số do chúng ta truyền vào, ở đây là chuỗi ‘Add to Cart’, phương thức mặc định là POST,
URL chuyển đến là tham số thứ 2, ở đây là chuỗi được tạo ra từ phương thức helper line_items_path(),
phương thức này được Rails định nghĩa tự động cho từng controller và sẽ trả về URL của lớp controller tương ứng, ví dụ như line_items_path()
trả về /line_item, cart_path()
trả về /cart
…v.v Phương thức này còn có thể nhận vào tham số và tự gắn vào đuôi như URL bình thường, ở đây chúng ta định nghĩa tham số :product_id
và truyền vào giá trị của biến product,
tức là id
của sản phẩm, và URL cuối cùng sẽ có dạng như /line_item?product_id=1, /line_item?product_id=2
…v.v
Và khi chúng ta click vào nút đó thì một trong số các phương thức routing của lớp LineItemsController
sẽ được gọi:
class LineItemsController < ApplicationController before_action :set_line_item, only: [:show, :edit, :update, :destroy] # GET /line_items # GET /line_items.json def index @line_items = LineItem.all end # GET /line_items/1 # GET /line_items/1.json def show end # GET /line_items/new def new end # GET /line_items/1/edit def edit end # POST /line_items # POST /line_items.json def create @line_item = LineItem.new(line_item_params) respond_to do |format| if @line_item.save format.html { redirect_to @line_item, notice: 'Line item was successfully created.' } format.json { render :show, status: :created, location: @line_item } else format.html { render :new } format.json { render json: @line_item.errors, status: :unprocessable_entity } end end end # PATCH/PUT /line_items/1 # PATCH/PUT /line_items/1.json def update respond_to do |format| if @line_item.update(line_item_params) format.html { redirect_to @line_item, notice: 'Line item was successfully updated.' } format.json { render :show, status: :ok, location: @line_item } else format.html { render :edit } format.json { render json: @line_item.errors, status: :unprocessable_entity } end end end # DELETE /line_items/1 # DELETE /line_items/1.json def destroy @line_item.destroy respond_to do |format| format.html { redirect_to line_items_url, notice: 'Line item was successfully destroyed.' } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_line_item @line_item = LineItem.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def line_item_params params.require(:line_item).permit(:product_id, :cart_id) end end
Có 2 phương thức có URL giống nhau (đều là /line_items
) là index
và create
, tuy nhiên phương thức được gọi sẽ là create,
bởi vì chỉ có create
mới dùng phương thức POST.
Tuy nhiên phương thức này đang chạy theo code mặc định của Rails, tức là tạo đối tượng LineItems
được gửi lên từ form ở /line_items/new,
bạn có thể trỏ vào dùng thử.
Chúng ta sửa lại phương thức create
để lấy từ nút button do chúng ta định nghĩa ở trên như sau:
class LineItemsController < ApplicationControler . . . # POST /line_items # POST /line_items.json def create @cart = current_cart product = Product.find(params[:product_id]) @line_item = @cart.line_items.build(:product => product) respond_to do |format| if @line_item.save format.html { redirect_to(@line_item.cart, :notice => 'Line item was successfully created') } format.json { render :show, status: :created, location: @line_item } else format.html { render :new } format.json { render json: @line_item.errors, status: :unprocessable_entity } end end end . . . end
Đầu tiên chúng ta tạo hoặc lấy một đối tượng Cart
trong session của người dùng từ phương thức current_cart
được định nghĩa trong lớp ApplicationController (application_controller.rb).
Sau đó chúng ta dùng id được truyền vào tham số :product_id
để lấy đối tượng Product
cụ thể,
tham số :product_id
được gửi vào biến params,
đây là biến được tạo ra khi chúng ta bấm vào nút ‘Add to Cart’ đã định nghĩa ở trên.
Trong đối tượng @cart
ở trên có đối tượng line_items,
đây là một đối tượng lưu trữ các phần tử theo dạng danh sách thuộc lớp ActiveRecord::Associations::CollectionProxy
, lớp này có một phương thức là build()
dùng để tạo một phần tử cho danh sách của nó. Chúng ta gọi phương thức này để tạo một đối tượng LineItem
mới, có tham số truyền vào là id của Product (:product_id)
rồi lưu vào biến @line_item.
Tiếp theo là đoạn respond_to
dùng để trả nội dung về cho người dùng, từ trước đến giờ chúng ta vẫn chưa tìm hiểu sâu về các đoạn respond_to
cũng như đối tượng format,
trong bài này chúng ta cũng sẽ chưa đi vào tìm hiểu làm gì, mình sẽ giải thích ở các bài sau, chỉ có một lưu ý là chúng ta gọi hàm redirect_to
đến URL /cart/<id>
dựa vào biến @line_item.cart,
thay vì trả về /line_items.
Chúng ta có thể chạy thử được rồi, bấm vào một nút ‘Add to Cart’ để thêm một sản phầm vào giỏ hàng.
Chúng ta có thể liệt kê danh sách các sản phẩm trong giỏ hàng ngay tại trang /cart/<id>
đó luôn bằng cách sửa file show.html.erb
trong thư mục app/views/carts
như sau:
<p id="notice"><%= notice %></p> <h2>Your cart</h2> <ul> <% @cart.line_items.each do |a| %> <li><%= a.product.title %></li> <% end %> </ul> <%= link_to 'Edit', edit_cart_path(@cart) %> | <%= link_to 'Back', carts_path %>
chào bạn, cho mình hỏi làm sao edit được thông tin trong giỏ hàng
Bạn vào trang mặc định của Rails là /carts nhé
Ý mình là edit số lượng của line item ấy, chứ vào cart nó thành edit cart rồi
Bạn đọc bài Migration mình có nói ấy 🙂
https://phocode.com/ruby/ruby-on-rails/rails-migration/
Cảm ơn bạn. Cho mình hỏi thêm là khi dùng button_to để post bài mới thì phải có create action của controller đó. Thế 1 page không thể có nhiều create action được phải không.
Thêm nữa là làm sao để click button sẽ gọi là 1 action or method của controller khác.
Nếu ý bạn là có nhiều action có tên là “create” thì đúng, ko thể có nhiều create action được Để một button gọi một action của controller bất kỳ thì bạn gán tham số thứ 2 của hàm button_to là URL của action đó, chẳng hạn bạn có controller là Dummy và action là index, thì bạn truyền vào URL của action này. Ở trên cái hàm line_items_path(…) trả về chuỗi ‘/line_items/1’ đó, mặc định thì Rails đã điều hướng cái URL này vào hàm create rồi. Vấn đề làm sao trỏ một URL vào một hàm nhất định… Đọc thêm »
Thế tức là page index của mình không thể vừa có 2 create action chẳng hạn
Action 1: add to cart (create method)
Actiion 2: like button (update true/false) cho items
Thêm nữa là, nếu tớ muốn tạo 1 button để delete all post
def xoa_het
Post.all.destroy_all
end
Làm sao để insert button đó và thực hiện method xoa_het được
Ý mình là bạn chỉ cần định nghĩa tên khác thôi, đừng để cùng tên đều là ‘create’ là được, create1, create2… chẳng hạn.
Còn phương thức xoa_het thì đầu tiên bạn vào file config/routes.rb, giả sử controller của bạn là PostsController thì sau đó thêm dòng này vào:
get '/xoa-het', to: 'posts#xoa_het'
Rồi ở hàm button_to bạn truyền là:
<%= button_to 'Xoa het', '/xoa-het' %>
Vậy là xong! Cái /xoa-het thì tùy bạn, muốn đặt thế nào cũng được.
Cảm ơn cậu rất nhiều, t rất thích các bài viết về rails của c.
Không có gì!
anh ơi cho em hỏi khi em truyền đường dẫn line_item_path(:product_id => product) vào trong button_to thì khi chạy nó báo lỗi missing require key id , bên line_items em đã viết lại method creat rồi ạ mà không được. mong anh giúp đỡ em là newbie ạ 🙂
Bạn kiểm tra xem code có sai chỗ nào không, sai chính tả, thiếu dấu đóng ngoặc mở ngoặc…v.v
Nếu không thấy thì bạn post code của view với controller lên đây mình xem.
chào anh, em làm theo từ đầu khóa học tới chỗ này thì hiện lỗi :
ActiveModel::UnknownAttributeError in LineItemsController#create
unknown attribute ‘cart_id’ for LineItem.
@cart = current_cart
product = Product.find(params[:product_id])
@line_item = @cart.line_items.build(:product => product) (dòng này bị note đỏ )
respond_to do |format|
if @line_item.save
anh có thể chỉ em cách khắc phục được không?
HI em, như thông báo lỗi đã ghi ra thì nó không tìm thấy thuộc tính cart_id. Em thử kiểm tra lại nha. Chẳng hạn như sai chính tả…v.v