Changing the number of fields(create, delete, and put) in a form【Rails】
Sometimes, you want to make this function. I did it for the first time. However, it was so difficult and confusing to me that I decided to take down how to implement it.
The goal I will achieve
The function is below.
In my case, the structure of tables is below.
The form I showed is for changing the info of investor(like name, age) and specialized_field at the same.
How to implement this function
Model
#Investor.rbclass Investor
has_many :specialized_fields
accepts_nested_attributes_for :specialized_fields, allow_destroy: trueend
and
#SpecializedField.rb
class SpecializedField < ApplicationRecord
belongs_to :investor
end
Controller
#Investors_controller.rb
class InvestorController def update
Investor.find(params[:id]).update(investor_params)
end private
def investor_params
params.require(:investor).permit(
:name,
:age,
specialized_fields_attributes: [:id, :contents, :_destroy
)
endend
View
#setting_investor_info.html.erb
<%= form_with model: @investor, url: user_settings_path, method: :put, class: "setting-investor-form", local: true do |i| %> <label class="label label--title">
specialized_fields
</label> <% @investor.specialized_fields.each do |sf| %> <%= i.fields_for :specialized_fields, sf do |s| %> <div class="form-group"> <div class="row"> <div class="form-group col"> <%= s.text_field :contents, class: "form-control input", value: sf.contents %> </div> <%= s.hidden_field :_destroy %> <%= link_to '#', class: 'ml-10 remove_fields' do %> <i class="fa fa-minus-circle"></i> delete <% end %> </div> </div> <% end %> <% end%> <div class="mt-3 mb-3"> <%= link_to_add_fields 'add specialized fields', i, :specialized_fields %> </div><% end %>
and
//UpdateInvestorInfo.js
//@flowimport $ from 'jquery';import JQueryView, { on } from '../../../common/JQueryView';export default class UpdateInvestorInfo extends JQueryView { constructor() { super('.setting-investor-form'); } @on({ event: 'click', selector: '.add_fields' }) onClickAddFields(event: any) { const time = new Date().getTime(); const regexp = new RegExp($(event.target).data('id'), 'g'); $(event.target).before( $(event.target) .data('fields') .replace(regexp, time), ); event.preventDefault(); } @on({ event: 'click', selector: '.remove_fields' }) onClickRemoveFields(event: any) { $(event.target) .prev('input[type=hidden]') .val('1'); $(event.target) .closest('.row') .hide(); event.preventDefault(); }}
and
#_specialized_field_fields.html.erb<div class="form-group"> <%= f.text_field :contents, class: "form-control input" %></div>
and, this is the most important code
#InvestorHelper.rbmodule InvestorHelper
def link_to_add_fields(name, f, association) new_object = f.object.send(association).klass.new id = new_object.object_id fields = f.fields_for(association, new_object, child_index: id) do |builder| render(association.to_s.singularize + '_fields', f: builder) end link_to('#', class: 'add_fields', data: { id: id, fields: fields.gsub('\n', '') }) do '<i class="fa fa-plus-circle""></i>'.html_safe + " #{name}" end
end
end
A thing I studied
When I implemented this function for the first time, I used FormObject. However, this is not suitable for it because it is difficult to implement “delete function and change the contents function”. Many references and communities say that we had better use FormObject when we want to change two or more databases. However, it is not necessarily right like this case. What technique we must choose is relative to the function we want to implement.