nested attribute rails

Giới thiệu

  • Nested attributes là kỹ thuật cho phép bạn lưu thuộc tính của bản ghi này thông qua bản ghi khác (associated records). Nói ví dụ đơn giản thì nếu bạn có một đối tượng A có liên kết với đối tượng B , thì bạn có thể khởi tạo , cập nhật đối tượng B thông qua đối tượng A . Kĩ thuật này rất hay được sử dụng trong các ứng dụng rails .

Sử dụng Nested Attributes

  • Để có thể sử dụng nested attributes thì bạn phải khai báo nó trong model . Thông qua phương thức accepts_nested_attributes_for trong model tương ứng.
class Job < ApplicationRecord
     has_many :images, as: :imageable, dependent: :destroy
    accepts_nested_attributes_for :images
end
  • Khi bạn sử dụng accepts_nested_attributes_for :images trong model Job thì khi create hoặc update cho đối tượng job bạn có thể create(update) luôn cho images bằng cách truyền thuộc tính của images vào job_params Trong controller job
def job_params
    params.require(:job).permit Job::ATTRIBUTES
  end

Trong model job

  ATTRIBUTES = [:title, :describe, images_attributes: [:id,
    :imageable_id, :imageable_type, :picture, :caption]]

Hoặc bạn cũng có thể chỉ viết ở trong controller như sau :

def job_params
    params.require(:job).permit :title,:describe, images_attributes: [:id,
    :imageable_id, :imageable_type, :picture, :caption]
  end

Sử dụng fields_for

Fields_for tạo ra một scope xung quanh một đối tượng cụ thể như form_for, nhưng không tạo các thẻ form. Điều này làm cho fields_for phù hợp để xác định các đối tượng mô hình bổ sung trong cùng một form của chính nó . Nói một cách đơn giản thì bạn dùng fields_for để xác định đối tượng mà bạn muốn khởi tạo hoặc cập nhật trong nested atttribute . Cụ thể hơn thì tôi sẽ sử dụng nó để tạo images cho job mà tôi đã khai báo ở trên

<%= form_for @job do |f| %>
    <%= f.fields_for :images do |ff| %>
          <%= ff.label :picture %>
          <%= ff.file_field :picture %>
    <% end %>
    <% end %>
    <%= f.submit "Save", method: :update,
      data: {confirm: "Are you sure"} %>
  <% end %>

#Sử dụng link_to_add_fields

  • Khi làm đến bước trên thì chúng ta cơ bản đã có thể sử dụng nested attribute rùi . Tuy nhiên nếu làm như phía trên của tôi thì sẽ có một câu hỏi đặt ra là khi bạn muốn tạo ra nhiều đối tượng images cho job thì sao ??.
  • Để trả lời cho câu hỏi này thì chúng ta có 2 cách :
  • Cách 1 đó là sử dụng for hoặc each . Đơn giản là bạn sử dụng vòng lặp , bạn muốn thêm bao nhiêu đối tượng images phía trên thì sẽ lặp bấy nhiêu lần , cách này có một nhược điểm là bạn phải gán cố định số vòng lặp tất nhiên nó sẽ làm cho tính tùy biến trong ứng dụng của chúng ta giảm xuống và tôi cũng sẽ không đề cập đến cách này ở đây
  • Cách 2: Sử dụng link_to_add_fields . Cách này sẽ tạo ra một button mà khi click sẽ cho phép bạn sinh ra một đối tượng mới . mà cụ thể ở đây là đối tượng images .
  • Mặc định thì rails ko có helper này . **Trong code js **bạn thêm function add_fields . Hàm này sẽ lặp lại một đối tượng mà bạn truyền vào với một id mới .
function add_fields(link, association, content) {  
    var new_id = new Date().getTime();  
    var regexp = new RegExp("new_" + association, "g");  
    $(link).parent().before(content.replace(regexp, new_id));  
}

Trong form của bạn:

<%= link_to_add_fields "Add a Contact", f, :images, "btn btn-primary", "Add a new Contact" %>

Trong application_helper

def link_to_add_fields(name, f, association, cssClass, title)  
  new_object = f.object.class.reflect_on_association(association).klass.new  
  fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|  
    render(association.to_s.singularize + "_fields", :f => builder)  
  end  
  link_to name, "#", :onclick => h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")"), :class => cssClass, :title => title 
end  

Tổng kết

Nested attributes là kĩ thuật được sử dụng rất nhiều trong các ứng dụng của rails . Với việc sử dụng kĩ thuật này kết hợp với fields_for và link_to_add_fields sẽ giúp cho bạn tạo ứng dụng một cách nhanh hơn , có khả năng tùy biến hơn và chuyên nghiệp hơn . Thank for reading 😃 !