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?

Community
  • 1
  • 1
Mel
  • 2,481
  • 26
  • 113
  • 273

2 Answers2

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.

akbarbin
  • 4,985
  • 1
  • 28
  • 31
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

Community
  • 1
  • 1
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • 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? – Mel Dec 30 '15 at 23:25
  • 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 #. I have no idea what that error means, but will ask a fresh question to seek help interpreting it. – Mel Dec 30 '15 at 23:51
  • Can you give me a link to a github repo? I'll take a look at it if that's okay – Richard Peck Dec 31 '15 at 00:03
  • Hi, it's a private repo. I don't have the pw to add others. – Mel Dec 31 '15 at 00:03