LoginSignup
5
5

More than 5 years have passed since last update.

Dynamic fields with rails

Last updated at Posted at 2015-08-26

This is an example of implementing dynamic fields of has_many associations.
We have two models Price and Product with has_many association as defined below.

price.rb
class Price < ActiveRecord::Base
  belongs_to :product
end
product.rb
class Product < ActiveRecord::Base
  has_many :prices
  accepts_nested_attributes_for :prices, allow_destroy: true

  def initialize(attrs=nil)
    super
    self.prices << Price.new if self.prices.empty?
  end
end

We define route for adding dynamic price fields.

routes.rb
    resources :products do
      get :new_price, on: :collection
    end

In controller, we make sure to include prices_attributes in permitted attributes.
We don't need any controller action for new_price since it will be processed directly by new_price.js.erb

products_controller.rb
  private
  def product_params
    params.require(:product).permit(:name, :other_product_fields...
                                     prices_attributes: [
                                       :id, :value, ...,
                                     ])
  end

This is the javascript for rendering new dynamic fields based on the next_index received.
We render the dynamic fields with child_index set as next_index. Also, next_index of new button is incremented.

new_price.js.erb
<% field = "" %>
<% form = "" %>

<%# build a single price field %>
<% form_for Product.new do |f| %>
  <% form = f %>
  <% f.fields_for :prices, Price.new, child_index: params[:next_index] do |fa,idx| %>
    <% field = fa %>
  <% end %>
<% end %>
$("#new-price").append("<%= escape_javascript(render(partial: "price", locals: {f: form, fa: field}))%>");
$("#new-price-btn").attr({href: "<%= escape_javascript(new_price_products_path(next_index: params[:next_index].to_i+1)) %>"})

In price partial file, _destroy checkbox is included so that we can remove dynamic fields on save.

_price.html.haml
/ Price fields ...

/ Actions
= fa.check_box :_destroy

Finally, we add new price button in product form.

_form.html.haml
=form_for @product do |f|
  / Product fields...
  =f.fields_for :prices do |fa,idx|
    / Price 
    = render partial: "price", locals: {f: f, fa: fa}
  / Add new price button
  = link_to new_price_products_path(next_index: f.object.prices.length), id: "new-price-btn", class: "btn btn-info btn-sm", remote: true do
    =t("helpers.links.new")
5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5