Sử dụng gem cocoon tạo nested field trong Rails

Nếu là một lập trình viên làm việc với Rails chắc các bạn không cảm thấy xa lạ gì với thuật ngữ nested attribute, trong các dự án thực tế việc sử dụng tính năng này của Rails cũng khá phổ biến. Trong bài viết này, mình sẽ giới thiệu đến các bạn 1 gem khá phổ biến và quen thuôc dùng để tạo nested field, đó là gem cocoon.

I. Nested field là gì?

Giả sử trong dự án, chúng ta có một quan hệ Hotel - Rooms là 1-n như sau models/hotel.rb

has_many :rooms

model/room.rb

belongs_to :hotel

Với yêu cầu đặt ra, khi manager tạo ra 1 khách sạn, trên form đó họ muốn tạo ra các room một cách linh họat theo cơ chế có thể tăng giảm số phòng khi tạo/sửa, lúc này tính năng nested form của Rails sẽ trở nên hữu ích. Nói một cách đơn giản, tính năng nested form giúp bạn có thể tạo đuợc 1 object cùng với các object con của nó trên cùng 1 form, việc này bao gồm cả thêm sửa xóa trong truờng hợp bản ghi đã đuợc tạo ra hoặc tạo mới.

II. Xây dựng form với nested field

Để xây dựng form, truớc hết chúng ta cần thêm dòng này vào model hotel. models/hotel.rb

accepts_nested_attributes_for :rooms

Dòng này cho phép model hotel có thể nested thêm model con là rooms. Trên controller, params gửi lên cũng đuợc viết theo kiểu lồng mảng như sau:

def hotel_params
  params.require(:hotel).permit(:name,  rooms_attributes: [:id, :number, :square])
end

Và cuối cùng, các bạn tạo 1 view với form nested để submit như sau:

<%= form_for @hotel do |f| %>
  <%= render 'shared/errors', object: @hotel %>
  <div>
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>

  <div>
    <p><strong>Room:</strong></p>

    <%= f.fields_for :rooms do |room| %>
      <div>
        <%= room.label :number %>
        <%= room.text_field :number %>

        <%= room.label :square %>
        <%= room.text_field :square %>
      </div>
    <% end %>
  </div>

  <%= f.submit %>
<% end %>

Sau khi build 1 bản ghi mới là hotel với con là room, trên view các bạn đã thấy form tạo mới với truờng name của hotel và 1 bản ghi con là room. Khi submit form, bản ghi khách sạn cùng với phòng đã đuợc tạo ra đồng thời. Validate thì này các bạn có thể viết trong từng model một cách dễ dàng. Lúc này câu hỏi đặt ra là làm sao chúng ta có thể thêm, bớt các phòng trong khách sạn 1 cách linh động? Với cách thông thuờng, chúng ta sẽ viết 1 helper nhằm xóa, thêm 1 field con, nhưng trong bài viết hôm nay tôi sử dụng gem cocoon. Gem này rất đơn giản và giúp tiết kiệm công sức của chúng ta trong việc tạo ra các field một cách linh động.

III. Sử dụng gem cocoon để chỉnh sửa các field con

Đầu tiên các bạn add thêm gem cocoon vào dự án:

gem "cocoon"

chạy bundle và add file cocoon vào js

//= require cocoon

Xong rồi, giờ để remove một phòng, chúng ta chỉ cần gọi hàm <%= link_to_remove_association%> như sau

<div class="nested-fields">
        <%= room.label :number %>
        <%= room.text_field :number %>

        <%= room.label :square %>
        <%= room.text_field :square %>
        <%= f.check_box :_destroy %>
         <%= link_to_remove_association "remove address", f %>
</div>

link_to_remove_association là phương thức hỗ trợ của Cocoon. Phương thức này tạo ra một liên kết xoá field. Muốn xóa field, bạn cần đánh dấu vào ô check box của field đó. Nếu cần yêu cầu đẹp hơn, ví dụ bấm button xóa để remove field, các bạn có thể sử dụng hidden field thay cho check box và viết sự kiện để ẩn các field con trên màn hình, miễn là params truyền lên của các bạn vẫn là _destroy.

Lưu ý: Cocoon sử dụng jquery để xác định field removed nên class nested-fields là bắt buộc để là "nested-fields". Tuơng tự, khi muốn add thêm field, các bạn sử dụng phương thức link_to_add_association của Cocoon. Render một link để tự động thêm nested fields.

     <div class="links">
        <%= link_to_add_association 'add room', f, :rooms %>
      </div>

Khá là đơn giản phải không nào 😄. Tuy có ưu điểm là rút gọn thời gian code, có nhiều config hay và dễ sử dụng, gem cocoon đôi khi chưa đáp ứng đuợc hết yêu cầu của dự án đề ra. Các bạn có thể chỉnh sửa hoặc viết thêm các hàm để phù hợp với yêu cầu của khách hàng. Cocoon sử dụng jquery nên việc viết thêm các hàm khác cũng khá là đơn giản. Trên đây là 1 số huớng dẫn cơ bản của mình về cách tạo form nested và sử dụng gem cocoon cho nested field, chi tiết hơn về cách sử dụng gem này, các bạn có thể xem doc của gem tại địa chỉ: https://github.com/nathanvda/cocoon. Cảm ơn các bạn đã theo dõi bài viết.


All Rights Reserved