Nested form trong rails
Bài đăng này đã không được cập nhật trong 6 năm
Tổng quan
Trong rails nếu chúng ta muốn tạo form lồng trong form thì chúng ta có thể dùng fields_for. Nhưng nó vẫn chưa đủ để chúng ta có thể làm việc dễ dàng. Có một số gem hỗ trợ rails tạo ra nested form này nhưng mình thấy gem cocoon là hỗ trợ đầy đủ chức năng nhất. Nên hôm nay mình sẽ giới thiệu với các bạn về cài đăì và cách dùng gem cocoon.
Cài đặt
-
Thêm vào Gemfile
gem "cocoon"
-
Chạy lệnh `bundle install'
-
Thêm vào file
application.js
nếu sử dụng Rails 3.1+/Rails 4/Rails 5//= require cocoon
Hoặc nếu dùng Rails 3.0.x
rails g cocoon:install
và thêm vào file layoutjavascript_include_tag :cocoon
Cách dùng
Giả sử có 2 model là Project và Task. Project có các thuộc tính name, description. Task có các thuộc tính description, done. 1 Project thì có nhiều Task.
Trong file model
class Project < ActiveRecord::Base
has_many :tasks
accepts_nested_attributes_for :tasks, reject_if: :all_blank, allow_destroy: true
end
class Task < ActiveRecord::Base
belongs_to :project
end
reject_if: :all_blank
có nghĩa là từ chối thêm nếu như tất cả task trống.
allow_destroy: true
tức cho phép chúng ta có thể xóa task.
Trong file products_controller.rb
def project_params
params.require(:project).permit :name, :description, tasks_attributes: [:id, :description, :done, :_destroy]
end
Ở đây _destroy
là virtual attribute. Khi mà _destroy
được thiết lập thì nested model sẽ được xóa.
Trong view
- Nếu sử dụng Formtastic. Trong
projects/_form partial
chúng ta viết:
= semantic_form_for @project do |f|
= f.inputs do
= f.input :name
= f.input :description
%h3 Tasks
#tasks
= f.semantic_fields_for :tasks do |task|
= render 'task_fields', f: task
.links
= link_to_add_association 'add task', f, :tasks
= f.actions do
= f.action :submit
Và trong _task_fields
partial viết như sau:
.nested-fields
= f.inputs do
= f.input :description
= f.input :done, as: :boolean
= link_to_remove_association "remove task", f
- Nếu sử dụng SimpleForm. Trong
projects/_form partial
chúng ta viết:
= simple_form_for @project do |f|
= f.input :name
= f.input :description
%h3 Tasks
#tasks
= f.simple_fields_for :tasks do |task|
= render 'task_fields', f: task
.links
= link_to_add_association 'add task', f, :tasks
= f.submit
Và trong _task_fields
partial viết như sau:
.nested-fields
= f.input :description
= f.input :done, as: :boolean
= link_to_remove_association "remove task", f
- Nếu sử dụng Standard Rails forms. Trong
projects/_form partial
chúng ta viết:
= form_for @project do |f|
.field
= f.label :name
%br
= f.text_field :name
.field
= f.label :description
%br
= f.text_field :description
%h3 Tasks
#tasks
= f.fields_for :tasks do |task|
= render 'task_fields', f: task
.links
= link_to_add_association 'add task', f, :tasks
= f.submit
Và trong _task_fields
partial viết như sau:
.nested-fields
.field
= f.label :description
%br
= f.text_field :description
.field
= f.check_box :done
= f.label :done
= link_to_remove_association "remove task", f
Các helper function của cocoon
Thì trong gem cocoon định nghĩa 2 helper function chính đó là link_to_add_association
và link_to_remove_association
link_to_add_association
Đây là sự kiện để thêm mới các trường của nested model vào trong form.
nếu không có= link_to_add_association 'add something', f, :task, partial: 'orther_partial'
partial: 'orther_partial'
thì mặc định nó sẽ lấy partial ở cùng thư mục và có tên làtask_fields
link_to_remove_association
Chức năng này dùng để xóa các trường của 1 nested model trong form
= link_to_remove_association 'remove task', f,
{ wrapper_class: 'my-wrapper-class' })
Ở đây wrapper_class
sẽ là class mà chứa các trường của nested model. Mặc định sẽ là nested-fields
. Các bạn nhớ đặt link_to_remove_association
vào trong cái class chứa trường của nested model luôn nhé.
Các sự kiện
Cocoon có một số sự kiện sau:
cocoon:before-insert: được gọi trước khi thêm, có thể hủy hành dộng thêm
cocoon:after-insert: được gọi sau khi thêm xong
cocoon:before-remove: được gọi trước khi xóa, có thể hủy hành động xóa
cocoon:after-remove: được gọi sau khi đã xóa
Các để bắt các sự kiện trên javascript
$(document).ready(function() {
$('#tasks')
.on('cocoon:before-insert', function(e, task_to_be_added) {
// do somthing
})
.on('cocoon:after-insert', function(e, added_task) {
// do somthing
})
.on('cocoon:before-remove', function(e, task_to_be_removed) {
// do somthing
});
.on('cocoon:after-remove', function(e, removed_task) {
// do somthing
});
});
Cảm ơn các bạn đã xem bài biết của mình. Happy codding
Bài viết được tham khảo từ https://github.com/nathanvda/cocoon
All rights reserved