I've recently started using Hotwire for a project I'm working on. Have been following this tutorial on GoRails. Currently having some issues with adding / updating records in a dynamic form. I have two models with a belongs_to
and has_many
relationship.
ProgramPart < ApplicationRecord
has_many :rich_contents, dependent: :destroy
accepts_nested_attributes_for :rich_contents, reject_if: :all_blank, allow_destroy: true
end
class RichContent < ApplicationRecord
belongs_to :program_part
has_rich_text :content
end
In a form I have a button where users dynamically can add RichContent
fields:
My relevant parts of my form for ProgramPart
looks like this:
<div id="block_elements" class="col-span-6 space-y-6">
<% @program_part.rich_contents.each do |content| %>
<%= render(RichContent::RichContentFormComponent.new(rich_content: content)) %>
<% end %>
</div>
<div class="col-span-6">
<span>
<%= link_to "Text and media", new_company_program_program_part_rich_content_path(@company, @program, @program_part.persisted? ? @program_part.id : Time.now.to_i), data: { turbo_stream: true } %>
</span>
</div>
<div class="bg-gray-50">
<%= part_form.submit class: "#{submit_classes}" %>
</div>
I have a .turbo_stream.erb
file where I render the form for my RichContent
:
<%= turbo_stream.append "block_elements" do %>
<%= render(RichContent::RichContentFormComponent.new(rich_content: @rich_content)) %>
<% end %>
# @rich_content = RichContent.new in controller
My ViewComponent
for that form looks like this:
<%= fields_for "program_part[rich_contents_attributes][#{@rich_content.persisted? ? @rich_content.id : Time.now.to_i }]", @rich_content do |rich_content_form| %>
<%= render(Forms::InputLabelComponent.new(label: "Text and media", form_builder: rich_content_form, model_attribute: :content)) do %>
<%= rich_content_form.rich_text_area :content %>
<% end %>
<% end %>
The problem I'm having is when editing ProgramParts
, updating existing values doesn't work and it creates new instances. Let's say if I have content "1", goes to edit, updates value to "2" submits the form, then it insert content "1" and "2" as new records.
The form params looks like this when submitting.
Processing by ProgramPartsController#update as TURBO_STREAM
14:28:07 web.1 | Parameters: {"authenticity_token"=>"[FILTERED]", "program_part"=>{"title"=>"Test", "description"=>"", "rich_contents_attributes"=>. {"152"=>{"content"=>"<div>2</div>"}}}, "commit"=>"Update Program part", "company_id"=>"1", "program_id"=>"9", "id"=>"27"}
Which gives the following result:
Any ideas on what I'm missing or why it doesn't update existing records and instead adds new records?
You need an id
attribute submitted with nested fields:
<%= fields_for "program_part[rich_contents_attributes][#{@rich_content.persisted? ? @rich_content.id : Time.now.to_i }]", @rich_content do |rich_content_form| %>
<%= render(Forms::InputLabelComponent.new(label: "Text and media", form_builder: rich_content_form, model_attribute: :content)) do %>
<%= rich_content_form.rich_text_area :content %>
<%= rich_content_form.hidden_field :id if rich_content_form.object.persisted? %>
<% end %>
<% end %>
The key in nested attributes is not used, it's just there to group fields together, which is why Time.now.to_i
can also be used:
"rich_contents_attributes" => {"152"=>{"content"=>"<div>2</div>"}
# ^ not used