Rails, Cocoon, Nested form not saving correctly - ruby-on-rails

Hi I have a nested form with rails and cocoon. I tested everything for hours and tried all suggestions in related topics but nothing worked. Here is my setup:
student.rb (Studen Model)
has_many :parents, dependent: :destroy
accepts_nested_attributes_for :parents,
reject_if: proc { |attributes|
attributes['first_name'].blank? },
:allow_destroy => true
students/new.html.erb
<div class="content <% if !#active_tab %> active <% end %>" id="tabStudent">
<%= form_for #student, validate: true do |student| %>
<%= render 'student_fields', model: student %>
<%= hidden_field(:active, 'tab', value: #active_tab) %>
</div>
<div class="content <% if #active_tab === 'tabParents' %> active <% end %>" id="tabParents">
<h2>Parents</h2>
<div id="parents">
<%= student.fields_for :parents do |parent| %>
<%= render partial: 'parent_fields', locals: {include_mode: 'new', f: parent, added_by: ''} %>
<% end %>
<%= link_to_add_association 'add', student, :parents,
render_options: { locals: {include_mode: 'new', added_by: 'rails'}},
'data-association-insertion-node' => '#parents',
'data-association-insertion-method' => 'append' %>
</div>
</div>
students_controller.rb
def create
#student = Student.new(student_params)
#student.parents.build
if #student.save
flash[:success] = "Success"
redirect_to #student
else
render 'new'
end
end
private
def student_params
params.require(:student).permit(:first_name, :last_name, :gender,
parents_attributes: [:id, :first_name, :last_name, :gender, :_destroy])
end
students/_parent_fields.html.erb
<div class="nested-fields">
<div class="row links">
<div class="large-12 columns right">
<%= link_to_remove_association raw('<i style="color:#ff0000;" class="fi-x-circle"></i>'), f %>
</div>
</div>
<div class="row">
<fieldset>
<legend>Data</legend>
<div class="row">
<div class="large-3 columns field">
<%= f.label :gender, 'Student Gender' %>
<%= f.select(:gender, [['male', 'm'], ['female', 'f']]) %>
</div>
<div class="large-3 columns field">
<%= f.label :first_name, 'First Name' %>
<%= f.text_field :first_name %>
</div>
<div class="large-3 columns field">
<%= f.label :last_name, 'Last Name' %>
<%= f.text_field :last_name %>
</div>
</fieldset>
</div>
</div>
It works so far that I can add and remove the form fields but when I send it, only the first parent is saved to the database. If I add a second parent with the "add" link an empty record is saved to the db. The second form also doesn't show up in the post data so it's no surprise that the record is empty but I just can't figure out why the data is not transmitted correctly.
EDIT: I located the problem. I use curb-foundation for the css styling and for the nested form I use their "tabs":
<div class="tabs-content">
<div class="content <% if !#active_tab %> active <% end %>" id="tabStudent">
<%= form_for #student, validate: true do |student| %>
<%= render 'student_fields', model: student %>
<%= hidden_field(:active, 'tab', value: #active_tab) %>
</div>
<div class="content <% if #active_tab === 'tabParents' %> active <% end %>" id="tabParents">
<h2>Parents</h2>
<div id="parents">
<%= student.fields_for :parents do |parent| %>
<%= render partial: 'parent_fields', locals: {include_mode: 'new', f: parent, added_by: ''} %>
<% end %>
<%= link_to_add_association 'new', student, :parents,
render_options: { locals: {include_mode: 'new', added_by: 'rails'}},
'data-association-insertion-node' => '#parents',
'data-association-insertion-method' => 'append' %>
</div>
</div>
The nested divs seem to break cocoon's function. If I remove the divs it works. Does anybody still know how I can work out a solution that allows me to use tabs?

Riddle solved. The empty record was produced because of the #student.parents.build. Cocoon does not require this command.
The main problem was solved by moving the #form_for part out of the first foundation tab one layer up so it would show up above every tab. Then I wrote a little query function to only present it to the user in the first tab.

Related

setting a select tag on Ruby on Rails

<%= form_tag("/posts/create") do %>
<div class="form">
<div class="form-body">
<% #post.errors.full_messages.each do |message| %>
<div class="form-error">
<%= message %>
</div>
<% end %>
<div class="continent">
<label>continent<br>
<%= #post.continent %>
<%= select :continent,
[["Africa","AF"],["North America","NA"],["Oceania","OC"],["Asia","AS"],["Europe","EU"],["South America","SA"]],
:prompt => "Select" %>
</label>
</div>
<div class="country">
<label>country<br>
<%= #post.country %>
<%= select :country,
[["Japan","JP"],["China","CH"]],
:prompt => "Select" %>
</label>
</div>
<div class="title">
<label>title<br>
<textarea name="title"><%= #post.title %></textarea>
</label>
</div>
<input type="submit" value="POST">
</div>
</div>
<% end %>
I want to set a select tag for choosing user's continent and country in the form
But It doesn't work well
I've tried some way to solve this problem.
And I just got the shape which is like "Select tag" but it's only included "prompt"
Please give me some advice.
FormOptionsHelper`s select takes these args
select(object, method, choices = nil, options = {}, html_options = {}, &block)
Try explicit putting options like this:
select :post, :country, [["Japan","JP"],["China","CH"]], { :prompt => "Select" }, { other_html_options }
If you are in a form context make this
<%= form_tag("/posts/create") do |form| %>
<%= form.select :country, [["Japan","JP"],["China","CH"]], { :prompt => "Select" }, { other_html_options } %>
<% end %>
First start off by creating the correct routes:
resources :posts
In Rails you create a resource by sending a POST request to the collection path ('/posts').
Then create the form with form_for(#post) or form_with(model: #post) (Rails 5.1+) and bind the model instance to the form builder.
<%= form_for(#post) do |f| %>
<div class="form">
<div class="form-body">
<% f.object.errors.full_messages.each do |message| %>
<div class="form-error">
<%= message %>
</div>
<% end %>
<div class="continent">
<%= f.label :continent do %>
<%= f.select :continent,
[["Africa","AF"],["North America","NA"],["Oceania","OC"],["Asia","AS"],["Europe","EU"],["South America","SA"]],
prompt: "Select" %>
<% end %>
</div>
<div class="country">
<%= f.label :country do %>
<%= f.select :country,
[["Japan","JP"],["China","CH"]],
:prompt => "Select" %>
<% end %>
</div>
<div class="title">
<%= f.label :title do %>
<%= f.text_area_field :title %>
</div>
<%= f.submit %>
</div>
</div>
<% end %>
This lets you reuse the same form for updating the resource. Also when you use the input helpers rails will properly name the inputs so that you can whitelist them with:
params.require(:post).permit(:continent, :country, :title)

Rails 5.2: How to create fields_for each I18n key value?

In my _form.html.erb I have nested fields, where for an Offer I would like to save multiple Discount types with values:
<%= f.fields_for #offer.discounts do |discount| %>
<% I18n.t(:discount_type).each do |type| %>
<div class="form-group row discount-list">
<label class="col-sm-8 control-label">
<%= discount.label I18n.t("discount_type.#{type[0]}") %><br/>
</label>
<div class="col-sm-4">
<%= discount.hidden_field :discount_type, value: type[0] %>
<%= discount.number_field :value,
value: (#offer.new_record? ? '0.00' : discount.value),
class: "form-control allow_numeric" %>
</div>
</div>
<% end %>
<% end %>
At the moment my form is populated correctly as I would like it to be, however values are not saving since:
in my params I see only 1 of 3 discount types like this:
"seller_discount"=>{"discount_type"=>"special", "value"=>"5"}
there is error Unpermitted parameter: :seller_discount
records are not saving
My Seller::Offer model looks like this:
has_many :offer_discounts, class_name: "Seller::OfferDiscount"
has_many :discounts, class_name: "Seller::Discount", through: :offer_discounts, inverse_of: :offers
accepts_nested_attributes_for :discounts, allow_destroy: true
My controller is simple as:
def new
#offer = Seller::Offer.new
end
private
def offer_params
params.require(:seller_offer).permit(
:company_id, :name, :base_price,
discounts_attributes: [:id, :discount_type, :value, :_destroy]
)
end
So far I've been trying different ideas from Rails docs, however no luck. Probably in my specific case, twist is where I try to iterate over I18n.t(:discount_type) an create input field for each discount type (buy key).
I'll be happy for any hint how to solve this. Thank you!
Since you're iterating over discount_type I think that needs to be an array type in your offer_params method.
def offer_params
params.require(:seller_offer).permit(
:company_id, :name, :base_price,
seller_discounts_attributes: [:id, :discount_types => [:discount_type, :value], :_destroy]
)
end
But what happens if you try to use fields_for helper?
<%= f.fields_for #offer.discounts do |discount| %>
<%= f.fields_for I18n.t(:discount_type) do |type| %>
<div class="form-group row discount-list">
<label class="col-sm-8 control-label">
<%= discount.label I18n.t("discount_type.#{type[0]}") %><br/>
</label>
<div class="col-sm-4">
<%= discount.hidden_field :discount_type, value: type[0] %>
<%= discount.number_field :value,
value: (#offer.new_record? ? '0.00' : discount.value),
class: "form-control allow_numeric" %>
</div>
</div>
<% end %>
<% end %>
So, to have my form working for both New and Edit actions, final solution is this:
<% if params[:action] == 'new' %>
<div class="col-md-7 col-sm-7">
<!-- Discounts for new form !-->
<% I18n.t(:discount_type).each do |type| %>
<%= f.fields_for :discounts, #offer.discounts.build do |disc| %>
<div class="form-group row discount-list">
<label class="col-sm-8 control-label">
<%= disc.label I18n.t("discount_type.#{type[0]}") %><br/>
</label>
<div class="col-sm-4">
<%= disc.hidden_field :discount_type, value: type[0] %>
<%= disc.number_field :value, value: '0.00',
class: "form-control allow_numeric" %>
</div>
</div>
<% end %>
<% end %>
</div>
<% elsif params[:action] == 'edit' %>
<div class="col-md-7 col-sm-7">
<!-- Discounts for edit form !-->
<%= f.simple_fields_for :discounts do |d| %>
<div class="form-group row discount-list">
<%= d.input :discount_type, as: :hidden %>
<label class="col-sm-8 control-label">
<%= d.label I18n.t("discount_type.#{d.object.discount_type}") %><br/>
</label>
<div class="col-sm-4">
<%= d.input :value, label: false, input_html: { id: d.object.discount_type+"_discount",
class: "form-control allow_numeric" } %>
</div>
</div>
<% end %>
</div>
<% end %>
Edit action is done with simple_form_fields_for
Obviously not shiny solution, but looks like this works.

Prevent creating blank associations in nested attributes forms

I have a form with nested attributes that is sending to the Appointments controller which is creating instances of associated models called Prescriptions, Illnesses, Allergies, and Immunizations. The idea is that a doctor can fill out the form and can add information where needed, but isn't required to fill out everything (not every appointment requires a prescription).
Currently, the form sends out and creates new instances of all the associations with all the attributes blank or nil. I tried adding a validation requiring the presence of the attributes to save, but that creates an error and then nothing can save.
How can I submit one form and prevent it from creating instances of associated models if the fields for that model were empty?
Appointments Controller
def create
#appointment = current_user.appointments.new(appointment_params)
set_associations(#appointment, #patient)
if #appointment.save
Notification.create_appointment_notifications(#appointment, #health_profile)
flash[:notice] = "Your appointment has been saved."
redirect_to patient_health_profile_path(#patient, #health_profile)
else
flash[:notice] = "There was a problem saving your appointment, please try again."
redirect_to patient_health_profile_path(#patient, #health_profile)
end
end
private
def appointment_params
symptoms = params[:appointment][:symptoms].split(",")
#patient = Patient.find(params[:patient_id])
params.require(:appointment).permit(
:diagnosis, :referrals, :notes, :symptoms,
immunizations_attributes: [:name, :expiration_date],
illnesses_attributes: [:name, :status],
allergies_attributes: [:name, :status, :severity],
prescriptions_attributes: [:medicine, :dosage, :refills, :expiration_date]
).merge(symptoms: symptoms,
patient: #patient
)
end
def set_associations(appointment, patient)
appointment.illnesses.each do |illness|
illness.patient = patient
end
appointment.allergies.each do |allergy|
allergy.patient = patient
end
appointment.immunizations.each do |immunization|
immunization.patient = patient
end
appointment.prescriptions.each do |prescription|
prescription.patient = patient
prescription.doctor = current_user
end
end
def set_information
#patient = Patient.find(params[:patient_id])
#health_profile = HealthProfile.find(params[:id])
end
Nested Form
<%= form_for #appointment do |f| %>
<div>
<h4>Appointment</h4>
<div>
<%= f.label :Symptoms %>
<%= f.text_field :symptoms, id: "symptoms-input" %>
</div>
<div>
<%= f.label :Notes %>
<%= f.text_area :notes %>
</div>
<div>
<%= f.label :Diagnosis %>
<%= f.text_field :diagnosis %>
</div>
<div>
<%= f.label :Referrals %>
<%= f.text_area :referrals, id: "referrals-input" %>
</div>
</div>
<div>
<h4>Illnesses</h4>
<div>
<%= f.fields_for :illnesses do |illness| %>
<%= render "appointments/illness_fields", f: illness %>
<% end %>
</div>
<div>
<%= link_to_add_association '+ Illness', f, :illnesses, partial: "appointments/illness_fields" %>
</div>
</div>
<br>
<div>
<h4>Allergies</h4>
<div>
<%= f.fields_for :allergies do |allergy| %>
<%= render "appointments/allergy_fields", f: allergy %>
<% end %>
</div>
<div>
<%= link_to_add_association '+ Allergy', f, :allergies, partial: "appointments/allergy_fields" %>
</div>
</div>
<br>
<div>
<h4>Immunizations</h4>
<div>
<div>
<%= f.fields_for :immunizations do |immunization| %>
<%= render "appointments/immunization_fields", f: immunization %>
<% end%>
</div>
<div>
<%= link_to_add_association '+ Immunization', f, :immunizations, partial: "appointments/immunization_fields" %>
</div>
</div>
</div>
<br>
<div>
<h4>Prescriptions</h4>
<div>
<%= f.fields_for :prescriptions do |prescription| %>
<%= render "appointments/prescription_fields", f: prescription %>
<% end %>
</div>
<div>
<%= link_to_add_association '+ Prescription', f, :prescriptions, partial: "appointments/prescription_fields" %>
</div>
</div>
<%= hidden_field_tag(:patient_id, params[:patient_id]) %>
<%= hidden_field_tag(:id, params[:id]) %>
<div>
<%= f.submit :Submit %>
</div>
<% end %>
In your Appointments Model, add this line:
accepts_nested_attributes_for :illnesses, reject_if: :all_blank, allow_destroy: true

How to dynamically Update/Edit/Delete existing record within a list

new to rails and could use some help figuring out how to allow users to update records in a list without having to leave the page.
Specifically, I have two forms on a page where users enter their children's info.
One form is for the user to add a NEW child's info to create a list of children below.
The list of children displays the user's previously entered children info.
However, within the child list I would like to allow users to both delete and edit an individual child's record.
My DELETE function is working fine, it's the UPDATE functionality I am having trouble with...
Here's the children#update controller:
def update
raise
#user = current_user
#child = Child.find(params[:id])
if #child.update_attributes(child_params)
flash[:notice] = "Child info was updated."
else
flash[:error] = "Sorry. Something went wrong, please try again."
end
respond_with(#child) do |f|
f.html { redirect_to new_child_path }
end
end
Here's the childlist form partial view:
<form role="form">
<% i = 1 %>
<% #user.children.each do |child| %>
<div class="col-md-12 form-align list-line">
<div class="col-md-10 form-align">
<%= label_tag child, "Child #{i}:" %>
<% i += 1 %>
</div>
</div>
<%= form_for(child, method: :put) do |f| %>
<div class="col-md-12 form-align">
<div class='col-md-4 form-align'>
<%= f.label :first_name, class: 'sr-only' %>
<%= f.text_field :first_name, value: child.first_name, class: 'form-control form-control-align' %>
</div>
<div class='col-md-4 form-align'>
<%= f.label :middle_name, class: "sr-only" %>
<%= f.text_field :middle_name, value: child.middle_name, class: 'form-control form-control-align' %>
</div>
<div class='col-md-4 form-align'>
<%= f.label :last_name, class: "sr-only" %>
<%= f.text_field :last_name, value: child.last_name, class: 'form-control form-control-align' %>
</div>
</div>
<div class="col-md-12 form-align">
<div class="col-md-4 form-group form-inline form-align">
<%= f.label :birth_date, "D.O.B." %>
<%= f.date_field :birth_date, value: child.birth_date, class: 'form-control' %>
</div>
<div class="col-md-4 form-group form-inline form-align">
<%= f.label :deceased, "Deceased?" %>
<%= f.select :deceased, value: child.deceased?, class: 'form-control form-control-align' %>
</div>
<%= f.submit "Update" %>
<%= link_to '<i class="glyphicon glyphicon-remove red"></i>'.html_safe, child, method: :delete %>
</div>
<% end %>
<% end %>
</form>
...and the child model: simply belongs_to :user / user model has_many :children
...and routes: resources :children
I think I need some options passed through my form_for, but unable to find what those need to be...
I would suggest using a gem like Best in Place to allow for in-place editing on certain fields in the list.
If you don't want in-place editing then utilize a modal view that contains the edit form.

Ruby on Rails, form helper with object method

I have a model with a method called date_string. The point of this method is to return a formatted date string when being used in a share view. Here is the view code.
<div class="field control-group">
<div class="control-label">
<%= f.label :business_hour , :date_string %>
</div>
I am expecting the f.label call to function like in this api doc, with :business_hour being the object, and :date_string being the method. However, the only thing that is rendered to the view is the string 'Date string' or 'date_string'.
Any help on getting a view class to call a method, not a property, on a model is greatly appreciated.
Business_Hour code
class BusinessHour < ActiveRecord::Base
attr_accessible :business_hourable_id,
:business_hourable_type,
:close_time, :day, :open_time,
:order , :business_date
belongs_to :business_hourable , :polymorphic => true
def date_string()
if business_date.nil?
return ''
else
return_string = business_date.strftime( '%a %b\ %e , %Y' )
end
end
end
Here is the full partial code(shared/business_hours):
<div class="field control-group">
<div class="control-label funkiness">
<%= F.label :business_hour , :date_string %>
</div>
<div class="controls">
<%= f.select :open_time, available_hours, :include_blank => true %>
</div>
<div class="control-label">
<%= f.label :open_time, 'Close Time' %>
</div>
<div class="controls">
<%= f.select :close_time, available_hours, :include_blank => true %>
</div>
</div>
Here is the pertinent part of the _form
<%= form_for (#some_show), html: {class: "form-horizontal pull-left"} do |f| %>
...
<%= f.fields_for :business_hours do |operating_time| %>
<%= render :partial => 'shared/business_hours',
:locals => {:f => operating_time} %>
<% end %>
And finally, here is the edit action of the controller
# GET /some_shows/1/edit
def edit
#some_show = SomeShow.find(params[:id])
end
So the solution that worked for me was to take the date_string off of my model, and implement it in a helper. Then, I modified my partial view so it looks like this:
<div class='field control-group hour_dropdown_2'>
<div class='field control-group'>
<div class="control-label">
<label><%= get_business_hour_string(f) %></label>
</div>
<h4 class='no-break-h'>From</h4>
<%= select :open_time, available_hours, :include_blank => false %>
<h4 class='no-break-h'>To</h4>
<%= select :close_time, available_hours, :include_blank => false %>
</div>
</div>
My select tags are still busted and I don't know why for my _address partial I get an f.whatever, but for the BusinessHour partial I get nothing in the way of form helpers.

Resources