Search code examples
ruby-on-rails-5nested-attributesupdating

How do I create a form that will update the value of a field for selected records?


I am using a link from an index page that has a group of nested records (row) that I need to update all at once. The link goes to an edit action that I need to make update the attributes of nested records (prisms).

  1. I tried using the simple_form gem methods for nested models. It gives me a field for all of the objects, when I only want one field to enter a value to them all. The builder from that looks usable, but I don't know how to use it to update the fields. Either way, the form isn't right.

  2. I have tried every variation of form_for and fields_for I could find on Google to develop the edit form. It looks like I'm the only one on Earth trying to solve this problem.

This is how I have my routes set up:

resources :gardens, shallow: true do
    resources :prisms
    resources :rows

Here is how my garden model is now:

class Garden < ApplicationRecord
  mount_uploader :picture, ImageUploader
  belongs_to :user
  has_one :photo
  has_many :rows, :dependent => :destroy
  has_many :prisms
  geocoded_by :address
  after_validation :geocode
  after_commit :populate_garden!

def id
    self[:id]
  end

  def populate_garden!
    # row 0
    (0..length-1).each do |i|
      Row.create(row_num: i, garden_id: id)
    end
  end
end

The garden model creates my rows through the populate_garden! method.

Here is the row model:

class Row < ApplicationRecord
  belongs_to :garden
  has_many :prisms, :dependent => :destroy
  accepts_nested_attributes_for :prisms
  after_commit :populate_rows

  def id
    self[:id]
  end


  def populate_rows
    # row 0
    (0..garden.width-1).each do |i|
        Prism.create(:row_id => self.id, :row_num => self.row_num, :col_num => i, :garden_id => self.garden_id)
    end
  end
end

The row model creates prisms in the populate_rows method.

Here is the prism model:

class Prism < ApplicationRecord
  belongs_to :row
  belongs_to :garden

  include RankedModel
  ranks :column_order
end

Here is the table from my index.html.erb that I click to open the edit action.

<table>

    <% @rows.each_with_index do |gardenrow, index| %>
      <% @rows.select { | row | row.row_num == index}.each do |row| %>

        <td class="prism-cols">
          <%= link_to 'Edit Row', edit_row_path(row), class:"dark-link" %>
          <br /><i class="fas fa-arrow-down"></i>
        </td>

      <% end %>
    <% end %>
</table>

The row passes nicely into the edit action, and I currently have this incorrect form:

<h1>The row brought into this form page is: </h1>
<%= @row.inspect %>

<div class="container">
  <%= simple_form_for @row do |m| %>
    <%= m.simple_fields_for :prisms do |p| %>
      <%= p.input :crop_name %>
    <% end %>
    <%= m.button :submit %>
  <% end %>
</div>

The rows_controller update method looks like this:

def update
    @row = Row.find(params[:row_id])
    @row.prisms.build
    redirect_to root_path
end

I need one form field for crop_name that will change all of the prisms in the selected row with a single submit. I don't have any problems updating one prism at a time through an edit action on the prism. The difficulty I'm having is working through the nesting of prisms inside of a specific row.

With the help of my mentor below I was able to develop a form that works with the controller to make this work. Here is the updated code for later use with this type of problem.

Here is the form data:

<%= form_tag({controller: "rows", action: "update"}, method: "patch") %>
    <%= label_tag(:crop_name, "Crop Name") %>
    <%= text_field_tag(:crop_name) %>
    <%= hidden_field_tag(:row_id, @row.id) %>
    <%= submit_tag("submit") %>

Here is the controller update method:

def update
    @row = Row.find(params[:id])
    @garden = Garden.find_by_id(:garden_id)
    @row.prisms.each do |p|
      p.crop_name = params[:crop_name]
      p.save!
    end
    redirect_to :controller => 'gardens', :action => 'show', id: @row.garden_id
  end

Thanks for the help. I don't think I could have figured this out from the documentation alone.


Solution

  • If I'm understanding correctly, I think simple_form may be limiting you. A basic ruby form may do what you want. I'm not 100% sure what the best way is to do a simple_form on nested fields but this stackoverflow answer may be able to help more.

    Using a basic ruby form You want a form that has one field. When submitted, it will take the value from the submitted form and update that field for all prisms of that row. I would recommend digging more into the basics of ruby forms for this kind of scenario and then do something like this.

    // html.erb
    
    <%= form_tag({controller: "rows", action: "update_prism_crop_name"}, method: "post", class: "nifty_form") %>
      <%= label_tag(:crop_name, "Crop name") %>
      <%= text_field_tag(:crop_name) %>
      <%= hidden_field_tag(:row_id, @row.id) %>
      <%= submit_tag("Submit") %>
    <% end %>
    
    
    // rows_controller
    
    def update_prism_crop_name
       @row = Row.find(params[:row_id])
       @row.prisms.each do |prism|
          prism.crop_name = params[:crop_name]
          prism.save!
       end
       # other redirect stuff
    end
    

    The form_tag explicitly calls out an action but I have to imagine that you'll need to build a route for this custom action as well.

    I haven't tested any of this and I'm a bit rusty in rails but I believe something like this would work.