newkidontheblock
newkidontheblock

Reputation: 65

Render New/Create and Edit/Update form on same page

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.

Boards#show

<%= 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 %>

Form partial

<%= 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

Answers (1)

Jonathan Bennett
Jonathan Bennett

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

Related Questions