UPDATE: Fixed, thanks for pointing it out kjmagic13. You can see the solution below.
So I've been working on this one since yesterday. I have a Rails project with a form_for collecting client data like name, age, etc. There's an "Add spouse?" button which uses javascript to reveal additional spouse input fields. All of these attributes are whitelisted, and the spouse data are actually no more than attributes on the client model (no spouse object).
The FIRST time you input data to these fields, they all pass through fine and get saved. Then when you revisit the page, the form is prepopulated with that data. You can edit the fields and update the client info, but NOT THE SPOUSE FIELDS --> even though those fields are whitelisted and are nothing more than client attributes. I can't even imagine how I'd make this happen on purpose, much less figure out how to fix it.
The form gets submitted, and the params passed in are always the old spouse data, not what went into the input field. However all the new client info IS updated in the same form. Looking in the logs always shows the old spouse data being passed through, as if I never typed anything
Here's the relevant snippets:
def update
if @client.report_ready?
outdate_report
end
respond_to do |format|
if @client.update(client_params)
format.html { redirect_to @client, notice: 'Client was successfully updated.' }
format.json { render :show, status: :ok, location: @client }
else
format.html { render :show }
format.json { render json: @client.errors, status: :unprocessable_entity }
end
end
end
def client_params
params.require(:client).permit(:first_name, :last_name, :email, :dob, :address, :city, :state, :zip, :phone, :retire_age, :ss_age, :user_id, :spouse_dob, :spouse_retire_age, :spouse_ss_age, :spouse_first_name, :spouse_last_name)
end
# the form in the view
<%= form_for (@client) do |f| %>
<% if @client.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@client.errors.count, "error") %> prohibited this plan from being saved:</h2>
<ul>
<% @client.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<div>
<%= f.label :first_name, class: 'left' %><br>
<%= f.text_field :first_name %>
</div>
<div>
<%= f.label :last_name, class: 'left' %><br>
<%= f.text_field :last_name %>
</div>
<div>
<%= f.label :email, class: 'left' %><br>
<%= f.email_field :email %>
</div>
<div>
<%= f.label :phone, class: 'left' %><br>
<%= f.phone_field :phone %>
</div>
<div>
<%= f.label :dob, 'Birthdate', class: 'left' %>
<%= f.date_field :dob, style: "font-size: .9em;" %>
</div>
<div>
<% if @client.dob.present? %>
<%= f.label :age, class: 'left' %><br>
<%= text_field_tag :age, @client.age, readonly: true %>
<% else %>
<%= hidden_field :age, value: nil %>
<% end %>
</div>
<div>
<%= f.label :retire_age, 'Ret. age', class: 'left' %><br>
<%= f.number_field :retire_age %>
</div>
<div>
<%= f.label :ss_age, 'S.S. age', class: 'left' %><br>
<%= f.number_field :ss_age %>
</div>
<div>
<%= f.submit "Save", class: "button radius tiny" %>
</div>
<% if !@client.new_record? && @client.has_spouse? %>
<%= render 'spouse_fields', f: f %>
<% else %>
<%= render 'spouse_button' %>
<% end %>
<span id="spouse_fields" style="display:none">
<%= render 'spouse_fields', f: f %>
</span>
# the spouse fields (which are really just a continuation of the form)
<hr>
<h1>Spouse Information</h1>
<div>
<%= f.label :spouse_first_name, 'First name', class: 'left' %><br>
<%= f.text_field :spouse_first_name %>
</div>
<div>
<%= f.label :spouse_last_name, 'Last name', class: 'left' %><br>
<%= f.text_field :spouse_last_name %>
</div>
<div>
<%= f.label :spouse_dob, 'Date of Birth', class: 'left' %><br>
<%= f.date_field :spouse_dob, style: "font-size: .9em;" %>
</div>
<div>
<% if @client.spouse_dob.present? %>
<%= f.label :spouse_age, 'Age', class: 'left' %><br>
<%= text_field_tag :spouse_age, @client.spouse_age, readonly: true%>
<% else %>
<%= hidden_field :spouse_age, value: nil %>
<% end %>
</div>
<div>
<%= f.label :spouse_retire_age, 'Ret. age', class: 'left' %><br>
<%= f.number_field :spouse_retire_age %>
</div>
<div>
<%= f.label :spouse_ss_age, 'S.S. age', class: 'left' %><br>
<%= f.number_field :spouse_ss_age %>
</div>
<div>
<%= f.submit "Save", class: "button radius tiny" %>
</div>
# the js for the 'Add spouse button' (just reveals fields, then disappears)
<text id="spouse_button" onclick="toggleFields()">
Add Spouse?
</text>
<script>
function toggleFields() {
document.getElementById("spouse_fields").style.cssText = "";
document.getElementById("spouse_button").style.cssText = "display:none";
};
</script>
All I had missed was the location of the hidden extra spouse fields. They needed to be moved into the if/else block above it, like this (but kjmagic13's 2-line solution is clearly better):
<% if !@client.new_record? && @client.has_spouse? %>
<%= render 'spouse_fields', f: f %>
<% else %>
<%= render 'spouse_button' %>
<span id="spouse_fields" style="display:none">
<%= render 'spouse_fields', f: f %>
</span>
<% end %>
You're duplicating the spouse_fields
partial when editing the record. There will be two elements with the ID of spouse_fields
and the Javascript will only toggle the first one it comes across. The other will remain hidden and is most likely the fields being passed to the server.
<% if !@client.new_record? && @client.has_spouse? %>
<%= render 'spouse_fields', f: f # renders here on edit %>
<% else %>
<%= render 'spouse_button' %>
<% end %>
<span id="spouse_fields" style="display:none">
<%= render 'spouse_fields', f: f # and renders here on edit %>
</span>
You'll want something more along the lines of this:
<%= render 'spouse_button' unless @client.has_spouse? %>
<%= content_tag :span, render('spouse_fields', f: f), style: ( @client.has_spouse? ? nil : 'display:none' ) -%>