12/08/2018, 14:32

Rails Nested Forms using jQuery and SimpleForm

Chúng ta có 3 model: Stock model: cổ phiếu trên thị trường chứng khoáng. Portfolio model: Danh mục đầu tư.(Danh mục đầu tư chứa nhiều tài sản) Assets model: Mỗi tài sản có nhiều cổ phiếu. class Stock < ActiveRecord::Base has_many :assets end class Portfolio < ActiveRecord::Base ...

Chúng ta có 3 model:

  • Stock model: cổ phiếu trên thị trường chứng khoáng.
  • Portfolio model: Danh mục đầu tư.(Danh mục đầu tư chứa nhiều tài sản)
  • Assets model: Mỗi tài sản có nhiều cổ phiếu.
class Stock < ActiveRecord::Base
  has_many :assets
end

class Portfolio < ActiveRecord::Base
  has_many :assets
end

class Asset < ActiveRecord::Base
  belongs_to :stock
  belongs_to :portfolio
end
  • models/portfolio.rb
class Portfolio < ActiveRecord::Base

  belongs_to :user
  has_many :assets

  attr_accessible :title, :assets_attributes
  accepts_nested_attributes_for :assets, allow_destroy: true

end

  • controllers/portfolios_controller.rb
class PortfoliosController < ApplicationController

  def new
    @portfolio = Portfolio.new
    @portfolio.assets.build
  end

  def edit
    @portfolio = current_user.portfolios.find params[:id]
    @portfolio.assets.build
  end

end
  • Giao diện cho nested attribute sẽ như sau:
  • views/portfolios/_form.html.erb
<%= simple_form_for @portfolio do |f| %>
  <%= f.input :title, label: 'Portfolio Title' %>
 
 <%= f.simple_fields_for :assets do |assets_form| %>
    <%= assets_form.association :stock %>
    <%= assets_form.input :amount, :input_html => { min: 0} %>
  <% end %>     
  <%= f.submit %>
<% end %>  

  • Đến đây, nếu bạn load form thì nó có thể làm việc tốt. Thế nhưng bạn chỉ có thể tạo một asset form cho một portfolio form. Vì thế, chúng ta sẽ sử dụng jquery để cho người dùng có thể thêm bao nhiêu assets tùy thích.
  • Ở đây, chúng ta sẽ đánh dấu các asset form để jquery có thể tìm thấy một cách dễ dàng. Chúng ta sẽ thêm class cho asset form và thêm button "Add Another Asset".
  • views/portfolios/_form.html.erb
<%= simple_form_for @portfolio do |f| %>
  <%= f.input :title, label: 'Portfolio Title' %>

<%= f.simple_fields_for :assets do |assets_form| %>
    < div = "duplicatable_nested_form">
      <%= assets_form.association :stock %>
      <%= assets_form.input :amount, :input_html => { min: 0 } %>
    </div>
  <% end %> 
  <%= link_to 'Add Another Asset', ', class: 'duplicate_nested_form' %>
  <%= f.submit %>
<% end %>
  • assets/javascripts/nested_forms.js
$(document).ready(function() {
    if $('.duplicatable_nested_form').length > 0 {
        nestedForm = $('.duplicatable_nested_form').last().clone(); //Tạo một bản sao của asset form
    }
    // Bắt sự kiện click button "Add Another Asset"
    $(document).on('click', '.duplicate_nested_form', function(e){
      e.preventDefault(); // Chặn hoạt động thẻ a
      lastNestedForm = $('.duplicatable_nested_form').last(); // Lấy ra asset form cuối cùng
      newNestedForm  = $(nestedForm).clone(); 
      formsOnPage    = $('.duplicatable_nested_form').length; // Lấy số lượng asset form
      // Thay đổi label
      $(newNestedForm).find('label').each(function(){
        oldLabel = $(this).attr('for');
        newLabel = oldLabel.replace(new RegExp(/_[0-9]+_/), "_#{formsOnPage}_");
        $(this).attr ('for', newLabel);
       });
      // Thay đổi id, name
      $(newNestedForm).find('select, input').each (function(){
        oldId = $(this).attr('id');
        newId = oldId.replace(new RegExp(/_[0-9]+_/), "_#{formsOnPage}_");
        $(this).attr('id', newId);
        oldName = $(this).attr('name');
        newName = oldName.replace(new RegExp(/[[0-9]+]/), "[#{formsOnPage}]");
        $(this).attr('name', newName);
       }); 
      $( newNestedForm ).insertAfter( lastNestedForm); // Thêm asset form mới
    });
  })
  • Thêm nút để bắt sự kiện xóa asset form *views/portfolios/_form.html.erb
<%= simple_form_for @portfolio do |f| %>
    <%= f.input :title, label: 'Portfolio Title' %>
    
    <%= f.simple_fields_for :assets do |assets_form| %>
        < div = "duplicatable_nested_form">
            <%= assets_form.association :stock %>
            <%= assets_form.input :amount, :input_html => { min: 0 } %>
            <%= link_to 'Remove', ', :remote => true, :class => 'destroy_duplicate_nested_form' %>  
        </div>
    <% end %> 
    <%= link_to 'Add Another Asset', ', class: 'duplicate_nested_form' %>
    <%= f.submit %>
<% end %>
  • assets/javascripts/nested_forms.js.coffee
// Bắt sự kiện click button "remove"
 $(document).on('click', '.destroy_duplicate_nested_form', function(){
     $(this).closest('.duplicatable_nested_form').slideUp().remove();
 )};
  • Nested attributes là một công nghệ mạnh mẽ để phát triển nhanh các forms phức tạp. Và có rất nhiều cách để có thể tận dụng tối đa những gì mà Nested attributes mang lại. Nhưng tôi nghĩ sử dụng jquery sẽ mang lại hiệu quả hơn cho nhưng người mới làm quen với ruby on rails.

Tham khảo: http://davidlesches.com/blog/rails-nested-forms-using-jquery-and-simpleform

0