Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: radio button selection for nested objects

I'm stuck with this simple selection task. I have this models:

#  id         :integer(4)      not null, primary key
#  category   :string(255)
#  content    :text
class Question < ActiveRecord::Base
    has_many :choices, :dependent => :destroy
    accepts_nested_attributes_for :choices
end

#  id          :integer(4)      not null, primary key
#  content     :text
#  correct     :boolean(1)
#  question_id :integer(4) 
class Choice < ActiveRecord::Base
    belongs_to :question
end

When I create a new question, I want to specify in a nested form not only the content of the Question, but even the content of 3 Answer objects, and select with a radio button which one is the correct answer. In the new action of the controller, I have this:

def new
    @title = "New Question"
    @question = Question.new
    3.times { @question.choices.build }

    respond_to do |format|
        format.html # new.html.erb
        format.xml  { render :xml => @question }
    end
end

This is the form code:

<%= simple_form_for @question do |question_form| %>
    <%= question_form.error_notification %>

    <div class="inputs">
    <%= question_form.input :content, :label => 'Question' %>
    <%= question_form.input :category, :collection => get_categories, :include_blank => false %>

    <% @question.choices.each do |choice| %>
        <%= question_form.fields_for :choices, choice do |choice_fields| %>
            <%= choice_fields.input :content, :label => 'Choice' %>
            <%= choice_fields.radio_button :correct, true %>
            <%= choice_fields.label :correct, 'Correct Answer' %>
        <% end %>
    <% end %>
    </div>

    <div class="actions">
    <%= question_form.button :submit %>
    </div>
<% end %>

The problem is that this code produce three radio buttons with different names: you can select more than one correct answer, and this is not the correct behaviour. The names of the three radio buttons are question[choices_attributes][0][correct], question[choices_attributes][1][correct] and question[choices_attributes][2][correct].

The question is: how can I create three radio buttons with the same name, in order to select one and only one correct answer? How can I create a correct params array, in order to save them in the create action in this way:

def create
    @question = Question.new(params[:question])
    # render or redirect stuff....
end

Thank you very much!

like image 706
oli-g Avatar asked Feb 23 '11 12:02

oli-g


3 Answers

You can use radio_button_tag and pass it your own name attribute.

Being that you are still in the choice_fields scope, you have access to a few methods that can expose the object you are working on, which will help you name your radio_button_tag properly.

The following code will give your radio button the proper name to be accepted as a nested attribute:

<%= radio_button_tag "question[choices_attributes][#{choice_fields.index}][correct]", true, choice_fields.object.correct %>

choice_fields.index gives you access to the proper index of the resource being created, so the first coice will have the name question[choices_attributes][0][correct], the second one question[choices_attributes][1][correct], etc.

choice_fields.object.correct gives you access to the current value so the correct radio button will be filled in for edit forms.

Update: The solution above is actually wrong, it gives each radio button a different name attribute so they don't end up working together (you can select multiple radio buttons at once).

The soltion I ended up going with was allong the following lines:

<%= radio_button_tag "question[choices_attributes][correct_choice]", choice_fields.index, choice_fields.object.correct %>

This gives each radio button the same name as the others, and a value equal to the index of the correct choice. The params hash ends up looking like this:

question: {choices_attributes: {
        "0": {//choice 0},
        "1": {//choice 1},
        etc...
    },
    correct_choice: //index of correct answer
}

I then updated my controller to manually update the correct choice as follows:

def create

    # Find the index of the correct choice in the params hash
    correct_index = params[:question][:correct_choice]

    # mark the question at the correct index as correct
    params[:question][:choiceses_attributes][correct_index][:correct] = true

    # Use the updated params hash to create a new Question/update an existing Question

    @question = Question.new(params[:question])
    # render or redirect stuff....
end
like image 96
Yechiel K Avatar answered Oct 11 '22 23:10

Yechiel K


You can pass in the name option to the radio_button method:

<%= choice_fields.radio_button :correct, true, :name => "choices_attributes" %>

(From the rails docs, http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html : radio_button(object_name, method, tag_value, options = {}) )

Hope this helps.

like image 38
Rachel Hogue Avatar answered Oct 11 '22 23:10

Rachel Hogue


This is a non-answer, but I'd recommend you try out Formtastic. It makes dealing with nested models like this STUPID EASY: https://github.com/justinfrench/formtastic

like image 28
Jeff Casimir Avatar answered Oct 11 '22 23:10

Jeff Casimir