1

I'm struggling with adding multiple option values to a rails form with collection select and I am wondering what is the best way to set this up with the forms that I have.

I currently have a dropdown form where the user can select a class and a student and hit submit to go to a page with specific options and information and additional forms based on the student that was selected. Here is the code I wrote for that form:

<%= form_for :student do |f| %>
  <div class="field form-group">
    <p>Select your cohort.</p>
    <%= f.label :cohort %>
    <%= collection_select(:student, :cohort_id,
          Cohort.order('name DESC'), :id, :name,
          { :include_blank => "Cohorts" },
          { class: "form-control", id: "cohort-dropdown" })
    %>
  </div>

  <div class="field form-group">
    <p>Select your name.</p>
    <%= f.label :name %>
    <%= collection_select(:student, :id,
          Student.order('name ASC'), :id, :name,
          { include_blank: "Students" },
          { class: "form-control", id: "student-by-cohort-dropdown" })
    %>
  </div>

  <div class="actions">
    <%= f.submit "View Student", :class => "btn btn-default"%>
  </div>
<% end %>

A sample of the params that get passed from this form is here:

{"utf8"=>"✓",
"authenticity_token"=>"ye65JZ/hjvpy58XQ5qAMe9oEQUuMdZjMaRn3dcYJhM/7R/1uGL9eAfJdX4A7rf6lvPUwyhVAnf7KItI9Xv+VSg==",
"student"=>{"id"=>"25"},
"commit"=>"Sign Up",
"controller"=>"welcome",
"action"=>"create"}

I wanted to make this a dynamic dropdown form so that when you select your cohort, the students dropdown form will only contain the names of the students in that class. For that I edited the above form students collection select to be:

<%= collection_select(:student, :cohort_id, 
      Student.order('name ASC'), :cohort_id, :name,
      { include_blank: "Students" },
      { class: "form-control", id: "student-by-cohort-dropdown" })
%>

And added the following jquery function to make the dropdown form dynamic:

function filterStudentByCohortListener(){
  var students = $("#student-by-cohort-dropdown").html();

  $("#cohort-dropdown").change(function() {
    var cohortID = $("#cohort-dropdown option:selected").val();
    var filterBy = "option[value='" + cohortID + "']";
    var options = $(students).filter(filterBy)

    if(cohortID != "") {
      $("#student-by-cohort-dropdown").html(options).prepend("<option value>Students</option>");
    }
  });
}

This works great! Except now of course, the params that are getting passed are:

{"utf8"=>"✓",
"authenticity_token"=>"ye65JZ/hjvpy58XQ5qAMe9oEQUuMdZjMaRn3dcYJhM/7R/1uGL9eAfJdX4A7rf6lvPUwyhVAnf7KItI9Xv+VSg==",
"student"=>{"cohort_id"=>"1"},
"commit"=>"Sign Up",
"controller"=>"welcome",
"action"=>"create"}

Which means that I cannot find my student in that controller method because I no longer have access to the individual student's id, just their cohort_id (which I needed to setup the dynamic dropdown form). I have been trying to find a way to create multiple options so that instead of the current html (sample below) for the students dropdown:

<select class="form-control" id="student-by-cohort-dropdown" name="student[cohort_id]">
  <option value="">Students</option>
  <option value="4">Suzy Peterson</option>
  <option value="3">Sally Johnson</option>
  <option value="1">Adam Smith</option>
</select>

I instead have something like:

<select class="form-control" id="student-by-cohort-dropdown" name="student[id]">
  <option value="">Students</option>
  <option value="1" cohort_id="1">Suzy Peterson</option>
  <option value="25" cohort_id="2">Sally Johnson</option>
  <option value="16" cohort_id="1">Adam Smith</option>
</select>

That way I have the student id in place for the params and the cohort_id for the dropdowns. I have already searched through and tried to apply information from the following resources (among others):

ruby on rails f.select options with custom attributes

Rails Formtastic: adding "data-" field to option tag

Can someone explain collection_select to me in clear, simple terms?

http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html

https://www.ruby-forum.com/topic/93662

But I can't seem to get anything to work for me and I feel like my form is setup a bit differently than the forms in those examples so I am having trouble applying the information I see there. I am hoping to get some advice on how to get my multiple option values set up, I am guessing I need to add something to my first options hash (the one where I have already placed {include_blank: "Students"}) but none of my attempts seem to be able to grab the id for the selected student so any advice at all would be very much appreciated. Thanks!

Community
  • 1
  • 1
NColey
  • 525
  • 1
  • 6
  • 18
  • Update! I was able to get it working by changing my form to use select instead of collection select: <%= select(:student, :id, Student.order('name ASC').map {|s| [s.name, s.id, {:'cohort-id' => s.cohort_id}]}, {include_blank: "Students"}, {class: "form-control", id: "student-by-cohort-dropdown"}) %> and then editing my javascript to look for cohort-id instead of value. I'm going to keep researching though to try and understand how I would do that with collection_select. – NColey Nov 30 '15 at 04:59

2 Answers2

0

This could be vastly simplified by doing this in two steps (two controllers and two forms) instead of one, and it wouldn't require any javascript.

In the first step, the student would choose a cohort from a dropdown and click "Next". In the second step, a dropdown would be populated by students in the selected cohort. If you included a hidden field with the cohort id in your student selection form, you would be sending both ids to a third controller of your choice.

This would also be much easier to modify in the future when your requirements change.

Brent Eicher
  • 1,050
  • 9
  • 14
  • Thanks, I do understand that but right now the dropdown for the student and the cohort does take you to another form (which takes you to another page) so it seems like too much for people to have to through form after form after form. I'm hoping to have this work on one page but I'm just not sure how to pass in the additional options. – NColey Nov 30 '15 at 04:18
  • For clarification, this form is part of a signup form. For this particular app, the users sort of already exist, their information was gathered by scraping my class's students welcome page. So the signup is actually an edit action in the controller rather than a create action. Because of this I need to gather the student and the cohort first (which is what the dropdown form is for) and then pass that to the signup form so it knows which student to 'edit' when creating their account. Which is part of why I'm hoping to avoid splitting this into another form, if that makes sense. – NColey Nov 30 '15 at 04:22
  • Just a quick update, I managed to get it working using select instead of collection_select but I really appreciated your response to my question! – NColey Nov 30 '15 at 05:03
0

The highest-rated answer on this question (which you linked to) answers your question: https://stackoverflow.com/a/6374301/179125

It's just a matter of producing an array like this:

[ [ "Suzy Peterson",  1, { 'data-cohort-id' => 1 },
  [ "Sally Johnson", 25, { 'data-cohort-id' => 2 },
  [ "Adam Smith",    16, { 'data-cohort-id' => 1 },
  # ...
]

...and then passing that array to options_for_select. In your case that'll look something like the below.

In your controller (don't do ActiveRecord queries in your view):

@students = Student.order('name ASC')

In your view:

<%
  student_options = @students.map do |s|
    [ s.name, s.id, { 'data-cohort-id' => s.cohort_id } ]
  end
%>
<%= select(:student, :id, options_for_select(student_options),
      { include_blank: "Students" }, { class: "form-control", id: "student-by-cohort-dropdown" })
%>

This will render HTML like the following:

<select class="form-control" id="student-by-cohort-dropdown" name="student[id]">
  <option value="">Students</option>
  <option value="1" data-cohort-id="1">Suzy Peterson</option>
  <option value="25" data-cohort-id="2">Sally Johnson</option>
  <option value="16" data-cohort-id="1">Adam Smith</option>
</select>

You could, of course, condense the view logic into a one-liner, but that would be very unkind to the next person who tries to read your code (yourself included).

Community
  • 1
  • 1
Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • Thank you! I responded above (before I saw your answer) that I managed to get it working with select instead of collection_select but that I wanted to keep exploring doing it with collection_select. Your answer was really helpful, as the resources I had been using were confusing to understand and apply to my forms (when I tried to use options for select I got a lot of errors because I wasn't quite implementing it correctly). I'm going to keep looking this over and going over the information you shared, so I can get a better understanding of how to do this with collection select. Thanks again! – NColey Nov 30 '15 at 05:02
  • In fact, I think `select` is a better choice here, since `collection_select` is intended for collections of ActiveRecord objects, but `student_options` is just an array. I'll update my answer accordingly. – Jordan Running Nov 30 '15 at 05:11