Search code examples
ruby-on-railssimple-formnested-formsnested-attributes

Nested Rails 4 Form Accessing Array


I have a nested form that has 3 fields repeated 7 times. I want to do a check that if the first field group is empty, then it will stop the process and return an error asking a user to fill in those fields.

I already have a check in that it will drop any empty field groups from the Create action in the controller, that is the "reject_if: => :all_blank" part of the model. However, it deletes the first entry, which I would instead rather run a check on. This code doesn't work, and I don't know where to go from here besides trying to refine the check_attendee_form method. Any help would be appreciated out there.

Here are the related Models:

class Registration < ActiveRecord::Base

# Database Relationships
belongs_to :event
belongs_to :user
has_many :attendees

# Form relationships
accepts_nested_attributes_for :attendees,
            :allow_destroy => true,
            :reject_if => :all_blank

class Attendee < ActiveRecord::Base

# Database relationships
belongs_to :event
belongs_to :user

delegate :event, :event_id, to: :registration

# Validations
validates :first_name, :last_name, presence: true

Controller Create and New Actions and Methods

def new
@registration = @event.registrations.new
7.times { @registration.attendees.build }
end

def create
 @registration = @event.registrations.new(registration_params)
 @registration.user = current_user
 check_attendee_form

respond_to do |format|
  if @registration.save
    format.html { redirect_to event_path(@event), notice: 'You are now registered.' }
    format.json { render :show, status: :created, location: @registration }
  else
    format.html { render :new }
    format.json { render json: @registration.errors, status: :unprocessable_entity }
  end
end
end

def check_registration
@check = Registration.find_by event_id: @event, user_id: current_user
if @check.nil?
 @registration = @event.registrations.new
 @registration.user = current_user
else
 redirect_to event_path(@event),
             notice: "You're already registered!"
end
end

def check_attendee_form

  @attendees_check = @registration.find_by(attendees_attributes: params[:first_name])

  if @attendees_check.first.nil?
  render :new, notice: "You need to put in your name at least!"
  else
  end
end

And finally, the essential form info:

<%= simple_form_for [@event, @registration], :html => { :class => 'form-horizontal' } do |f| %>

<%= render 'shared/errors', object: @registration %>

<%= f.simple_fields_for :attendees, defaults: { input_html: { class: 'form-horizontal' } }  do |a| %>

<div>

<%= a.label :first_name %>:
<%= a.text_field :first_name %>
<%= error_span([:first_name]) %>
<%= a.label :last_name %>:
<%= a.text_field :last_name %>
<%= error_span([:last_name]) %>
<%= a.label :fundraising_goal %>:
<%= a.number_field :fundraising_goal, placeholder: 'No Commas' %>
<%= error_span([:fundraising_goal]) %>

Here are the params that are getting submitted:

{"utf8"=>"✓",
"authenticity_token"=>"dNR5QCBFplsAG0wzy87+hzaKuG6h2Mlb6xpmKEM0Kko=",
"registration"=>{"attendees_attributes"=>{"0"=>{"first_name"=>"",
"last_name"=>"",
"fundraising_goal"=>""},
"1"=>{"first_name"=>"",
"last_name"=>"",
"fundraising_goal"=>""},
"2"=>{"first_name"=>"",
"last_name"=>"",
"fundraising_goal"=>""},
"3"=>{"first_name"=>"",
"last_name"=>"",
"fundraising_goal"=>""},
"4"=>{"first_name"=>"",
"last_name"=>"",
"fundraising_goal"=>""},
"5"=>{"first_name"=>"",
"last_name"=>"",
"fundraising_goal"=>""},
"6"=>{"first_name"=>"",
"last_name"=>"",
"fundraising_goal"=>""}}},
"commit"=>"Create Registration",
"event_id"=>"5"}

How do I access the first array object with attendees_attributes of ID [0] in this form submission?


Solution

  • If you want to ensure that at least one Attendee is entered for a registration, I believe you could drop the check_attendee_form method and add the following validation to registration.rb:

    validate :check_minimum_attendees
    
    def check_minimum_attendees
        errors.add(:attendees, 'must be entered') if attendees.size == 0
    end