Reputation: 65
I have two controllers, Boards
and Sections
. Sections
are nested in Boards
and one Board
can have many Sections
. On the Show page of Boards
I want to have the ability to both create a new Section
and edit it.
Currently I have the form to create a new Section
on the Boards
show page but I haven't found a way to reuse this form for the edit method.
<%= render 'sections/shared/form', board: @board, section: @section %>
<br>
<% @board.sections.order(id: :asc).each_with_index do |section, index| %>
<%= "#{index + 1}. #{section.title}" %>
<% if policy(section).edit? %>
<%= link_to "Edit", edit_board_section_path(@board, @board.sections[index]) %>
<% end %>
<% if policy(section).destroy? %>
<%= link_to("Delete", board_section_path(@board, @board.sections[index]), method: :delete) %>
<% end %>
<br>
<% end %>
<%= simple_form_for([@board, @section]) do |f| %>
<%= f.input :title %>
<%= f.submit "Save" %>
<% end %>
The edit_board_section_path
basically duplicates the form partial.
How can I make it that the Edit button next to a section allows me to edit a section on the Boards#show page itself? I don't want to reconnect to another page to edit the form but display it directly on Boards#show.
Upvotes: 0
Views: 1554
Reputation: 1065
You will want to change the form partial to use the passed in variables, board
and section
, not the variables prefixed with @
. Adding remote: true
will let you submit via AJAX.
<%= simple_form_for([board, section], remote: true) do |f| %>
<%= f.input :title %>
<%= f.submit "Save" %>
<% end %>
With that change made, the render for the new section will still work as you currently have. To add an edit form for each existing section you can do the following:
<%# index.html.erb %>
<div id="sections">
<% @board.sections.order(id: :asc).each_with_index do |section, index| %>
<%= render partial: 'sections/section', section: section, index: index %>
<% end %>
</div>
<script>
window.addEventListener("click", function(event) {
if (event.target.matches('.edit-form-toggle')) {
event.target.parentElement.querySelector('.edit-form').classList.toggle('hidden');
}
});
</script>
<style>
.hidden { display: none; }
</style>
<%# sections/_section.html.erb %>
<div id="section-<%= section.id %>
<%= "#{index + 1}. #{section.title}" %>
<% if policy(section).edit? %>
<button class="edit-form-toggle">Edit</button>
<div class="edit-form hidden">
<%= render 'sections/shared/form', board: section.board, section: section %>
</div>
<% end %>
<% if policy(section).destroy? %>
<%= link_to("Delete", board_section_path(section.board, sections.id), method: :delete) %>
<% end %>
</div>
The board variable should be nil
if you are using shallow routes, and @board
if you are not, in the per-section edit form partial.
You will need to handle the different response formats in your controller:
class SectionsController < ApplicationController
def create
@board = Board.find(params[:board_id]
@section = board.sections.build(section_params)
respond_to do |format|
if @section.save
format.html { redirect_to @board }
format.js # renders app/views/sections/create.js.erb
else
format.html {}
format.js
end
end
end
def update
@section = Sections.find(params[:id])
respond_to do |format|
if @section.update(section_params)
format.html { redirect_to section.board }
format.js # renders app/views/sections/update.js.erb
else
format.html {}
format.js
end
end
end
def section_params
...
end
end
And your views:
// create.js.erb
<% if @section.errors %>
alert('could not save section');
<% else %>
// clear new form, append new section
document.getElementById('new_section').querySelector('input[name="section[name]"').value = "";
document.getElementById('sections').innerHTML += "<%= j(render 'section', section: @section, index: @section.board.sections.count -1) %>"
<% end %>
// update.js.erb
<% if @section.errors %>
alert('could not save section');
<% else %>
alert('saved section');
<% end %>
Upvotes: 1