Changing the number of fields(create, delete, and put) in a form【Rails】

Tomoharu Tsutsumi
3 min readSep 22, 2019

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.

The default view
When you click “add specialized fields”…
You input something and save…
When you click “delete” and save…
Of course, you can save and delete some fields at the same time
Moreover, you can change the contents and save

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: true
end

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
)
end
end

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
//@flow
import $ 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.

If you think this article good, please follow me!

I update articles once a week!

https://twitter.com/tomoharutsutsum

--

--

Tomoharu Tsutsumi
Tomoharu Tsutsumi

Written by Tomoharu Tsutsumi

5+ years Full Stack SWE (Ruby, Go, TypeScript, JavaScript) | Former Founding Engineer of AI Startup in Canada

No responses yet