ruby-on-railsnested-formsaccepts-nested-attributes

Rails simple form submits but nested fields from data doesn't persist


I'm working on a rails CMS project and I am trying to create from the admin dashboard, a quotes section where users can add a section_quote with title, image and five quotes with their authors(5 inputs for quotes and 5 for authors).

The issue I have is when I submit the form, all the data are saved and they persist in the form section except for data in the nested form. I found many topics here almost related to this case but none resolved my issue.

Quote model and its migration:

class Quote < ApplicationRecord

  #RELATIONS
  belongs_to :section, inverse_of: :quotes, :class_name => 'SectionQuote', foreign_key: :section_id

  #VALIDATIONS
  validates :quote_text, :author, presence: true
end

class CreateQuotes < ActiveRecord::Migration[6.1]
  def change
    create_table :quotes, id: :uuid do |t|
       t.string :author
       t.text :quote_text

       t.timestamps
    end

    add_column :quotes, :section_id, :uuid
  end
end

My section_quote model and section migration:

class SectionQuote < Section
  has_many :quotes, :class_name => 'Quote', inverse_of: :section, foreign_key: :section_id
  accepts_nested_attributes_for :quotes, allow_destroy: true, reject_if: :all_blank

  validates :title, presence: true
  validates :image, presence: true

end

class CreateSections < ActiveRecord::Migration[6.1]
  def change
    create_table :sections, id: :uuid do |t|
      t.string :type
      t.uuid :page_id

      t.timestamps
    end
   end
 end

Section_controller:

module Admin
  # administrative controller for Sections

  class SectionsController < Admin::AdminController
    before_action :load_section, only: [:update, :destroy, :show]

    def create
       @section = Section.new(build_params)
         authorize! :create, @section

        if @section.save
            flash.now[:success] = 'Sezione creata con successo'
        else
          flash.now[:error] = 'Ci sono degli errori nei dati inseriti. Si prega di 
          controllare'
        end

       respond_to do |format|
         format.js{  }
       end
  end

  def update
    authorize! :update, @section
    if @section.update(update_params)
       flash.now[:success] = 'Sezione aggiornata con successo'
    else
       flash.now[:error] = 'Ci sono degli errori nei dati inseriti. Si prega di controllare'
    end

    respond_to do |format|
      format.js{  }
    end
 end

 def destroy
   authorize! :destroy, @section
   @section.destroy
     flash.now[:success] = 'Sezione eliminata correttamente'
   respond_to do |format|
     format.js{  }
   end
 end

 def section
   @section_type = params[:section_type]

   section_klass = @section_type.camelize.constantize
   @page = Page.find(params[:page_id])
   @section = section_klass.new(page_id: params[:page_id])
 end

 private

 def load_section
   @section = Section.find(params[:id])
 end

 # Build params

 def build_params
   params.require(:section).permit(params_list)
 end

 def update_params
   params.require(:section).permit(params_list)
 end

 def params_list
   [
    :page_id,
    :html_identifier,
    :title,
    :subtitle,
    :description,
    :image,
    :image_alt,
    :image_position,
    :article_list_type,
    quotes_attributes: [:id, :author, :quote_text, :_destroy],
   ]
  end
 end
end

Partial Section_quote form in haml using simple_form.

- section ||= @section

  = simple_form_for [:admin, section], as: :section, url: section.persisted? ? 
    admin_section_path(section) : admin_sections_path, remote: true, format: :js, 
    multipart: true do |f|
        = f.input :page_id, as: :hidden
        = f.input :type, as: :hidden
        = f.input :html_identifier
        = f.input :title

        =f.simple_fields_for :quotes, Quote.new do |quote_form|
            = quote_form.input :author
            = quote_form.input :quote_text

        = f.input :image, hint: 'Dimensioni: 690x920px'
        .row
            .col-xs-12.col-md-6
                = webp_image_tag section, {width: 400, height: 210, class:  'mb-1 img-fluid'}
            .col-xs-12.col-md-6
                = f.input :image_alt
       
        = f.submit class: 'btn btn btn-primary'

What I expect is to also preserve the quotes data in the section form so it can render to the view layout.


Solution

  • The problem is:

    = f.simple_fields_for :quotes, Quote.new do |quote_form|
    

    You have a one to many relation but you're passing a single instance of Quote to the helper. Creating model instances in your view is actually an anti-pattern. You'll lose any user input when re-rendering the form after an invalid form submission. The view should take its data from the controller and turn it into markup in the simplest way possible - it should not be concerned with instanciating models.

    Instead you should "seed" the assocation in your controller:

    module Admin
      class SectionsController < Admin::AdminController
        # ...
       
        def new
          @section = Section.new
          3.times do
            @section.quotes.new
          end
        end
      end
    end
    

    And just call the assocation in your view:

    = f.simple_fields_for :quotes do |quote_form|