1

I am making an app in Rails 4. I use Simple Form.

I have a profile model and a qualifications model.

The associations are:

profile.rb

belongs_to :profile

qualifications.rb

has_many :qualifications  

I have a form in my profile views, which includes a part of a form from my qualifications view.

profiles#form

<%= simple_form_for(@profile) do |f| %>
            <%= f.error_notification %>

              <div class="form-inputs">

          <div class="row">

            <div class="intpol2">
              Your professional qualifications
            </div>

              <%= render 'qualifications/form', f: f %>     

          </div>

Qualifications#form

<%= simple_fields_for :qualification do |f| %>

  <div class="form-inputs">


    <div class="row">
        <div class="col-md-6">
            <%= f.input :title, :label => "Your award" %> 
        </div>

        <div class="col-md-6">


        </div>


    </div>

    <div class="row">
        <div class="col-md-6">
            <%= f.input :level,   collection: [ "Bachelor's degree", "Master's degree", "Ph.D", "Post Doctoral award"] %>
        </div>


        <div class="col-md-6">
        <%= f.input :year_earned, :label => "When did you graduate?", collection: (Date.today.year - 50)..(Date.today.year) %>
        </div>

  </div>

Users may have more than one degree. I want to add a field that is a button which says 'add another qualification' and then a new set of the qualification form fields is available.

I found this post which tries to do something slightly different. I don't want 10 blank sets of the form field (it will make the form look too long).

Creating multiple records for a model in a single view in Rails

Is there another way to achieve this?

2 Answers 2

0

You'll be looking for a gem called cocoon; you can also watch this Railscast (Nested forms) which is woefully outdated but still explains the structure very well.


The pattern is very simple, but requires some extra parts:

  1. Have an ajax button which calls the controller
  2. The controller needs to return a form and built fields_for
  3. You'll use JS to append the new fields_for to the original form

The biggest problem is the id of your new fields_for - new implementations of this pattern use child_index: Time.now.to_i

I've written about this here.

Here's a new version:


Ajax

Firstly, you need an "Add Qualification" button, which links to your controller through ajax:

#app/views/profiles/_form.html.erb
<%= simple_form_for(@profile) do |f| %>
    <%= f.error_notification %>
    <div class="form-inputs">
       <div class="row">
         <div class="intpol2">Your professional qualifications</div>
         <%= render 'qualifications/form', f: f %>     
       </div>
    </div>
       <%= button_to "+", new_profile_path, method: :get %>
<% end %>

Controller

This will go through the new controller method, which we should be able to manage to return the specific response for the ajax request:

#app/controllers/profiles_controller.rb
class ProfilesController < ApplicationController
   respond_to :js, :html, only: :new
   def new
      @profile = Profile.new
      @profile.qualifications.build
      respond_with @profile #-> will invoke app/views/profiles/new.js.erb
   end
end

Response

Once your new.js.erb has fired, we need to build the new HTML form, extract the fields_for and append it to your view:

#app/views/profiles/new.js.erb
var fields_for = $("<%=j render "profiles/form" %>").html(); //-> might need tweaking
$(fields_for).appendTo("#new_profile.form-inputs");

child_index

You should also change your qualifications/form to include the child_index:

#app/views/qualifications/form.html.erb
<%= simple_fields_for :qualifications, child_index: Time.now.to_i do ...

Child index is meant to denote the index of the fields_for elements. In our case (since we're making all new records), it doesn't matter. Using Time.now.to_i ensures a totally unique id each time.


Finally, you need to make sure you're calling:

 <%= simple_fields_for :qualifications ... %> 

... plural

Sign up to request clarification or add additional context in comments.

4 Comments

Hi Rich, Thanks very much for this. It's all really clear and helpful and makes a lot of sense. Before I use cocoon. I tried to follow your steps. The problem I have is where I want to edit the existing profile to add a new qualification, I get an error with the <%= button_to "+", new_profile_path, method: :get %>. If I change that to update, then do I need the controller action to be repeated in the update field, or does it somehow know to look for it in the new action?
I have reversed the steps above and tried using cocoon, but this: <%= link_to_add_association 'Add a qualification', f: f, partial: 'qualifications/qualification_fields' %> gives an undefined method `object' for #<Hash:0x007fe70a4b38d8>. I have no idea what that error means, but will ask a fresh question to seek help interpreting it.
Can you give me a link to a github repo? I'll take a look at it if that's okay
Hi, it's a private repo. I don't have the pw to add others.
0

It seems like that you have to use nested form. You have to try your link tutorial because I will use this too. For another tutorial you can use this as reference nested_forms-rails-4.2.

I hope this help you.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.