ruby-on-railsformsnested-attributesaccepts-nested-attributesform-with

Rails _destroy field appearing twice in form


I have two models, Preset and Plot, as below:

class Preset < ApplicationRecord
    belongs_to :user
    has_many :plots, :dependent => :destroy
    accepts_nested_attributes_for :plots, allow_destroy: true
end

class Plot < ApplicationRecord
    belongs_to :preset
    belongs_to :theme, optional: true
end

And a nested form for editing presets:

= form_with(model: @preset, local: true, method: "patch") do |f|
  = label_tag(:preset_name, "Preset name:")
  = text_field_tag(:preset_name, @preset.name)
  %br
  = f.fields_for :plots do |builder|
    %br
    = render 'editplot', f: builder
  %br

A partial _editplot that defines the checkbox for destroying the plot, as per railscast 196:

= f.label(:name, "Change plot:")
= f.select(:name, options_for_select([['Existing Plot 1', 'Existing Plot 1'], ['Existing Plot 2', 'Existing Plot 2']]))
= f.label(:_destroy, "Remove plot")
= f.check_box(:_destroy)

I have allowed the _destroy parameter in the presets controller

def preset_params
            params.require(:preset).permit(:name, plots_attributes: [:id, :name, :parameter_path, :theme_id, :_destroy])
        end

All other aspects of editing the presets work fine, but the checkbox for _destroy does not. The parameters for destroying one of two plots on the edit screen are displayed in console as follows:

Parameters: {"authenticity_token"=>"TOKEN", "preset_name"=>"Preset", "preset"=>{"plots_attributes"=>{"0"=>{"name"=>"Existing Plot 1", "_destroy"=>"1", "id"=>"16"}, "1"=>{"name"=>"Existing Plot 1", "_destroy"=>"0", "id"=>"17"}}}, "commit"=>"Update Preset", "id"=>"25"}

The presence of "_destroy"=>"1" suggests this is working as intended. However, when inspecting the page with Chrome Dev tools it shows there is also a hidden field <input name="preset[plots_attributes][0][_destroy]" type="hidden" value="0"> alongside the checkbox, whose _destroy value of 0 is also passed when the form is submitted. I have a feeling that this element is interfering with the form, but I'm not sure where it's come from or how to get rid of it.

I haven't included it here, but I have some JS code in the same form that adds and removes 'new plot' partials, and these generate their own _destroy fields. I didn't think they would be the cause of the issue, but I can add this code in an edit if necessary.


Solution

  • The issue was not with the checkbox, as pointed out by zwippie, but with my controller. I was trying to update the attributes of the preset and plots manually within the controller (i.e. using lines like @plot.update(name: plot_name, parameter_path: _parameter_path)). Because I was doing this manually, I wasn't actually handling the _destroy parameter and therefore rails wasn't doing anything with it once it had been passed from the form.

    To fix this, I used @preset.update(preset_params) instead, where preset_params represents the permitted parameters within the controller. As long as _destroy is permitted, it deletes the object.

    def preset_params
                params.require(:preset).permit(:name, plots_attributes: [:id, :name, :parameter_path, :theme_id, :_destroy])
            end