I am trying to implement a HABTM checkbox in a nested form.
Currently, I have 3 models. Subject, lesson and groups.
The associations are as follows:
Each subject has many lessons.
Each lesson has and belongs to many groups.
Right now, I am trying to implement them all on a single creation and edit form.
Such that a lesson is nested in the subject and for each lesson there is a list of group check boxes to implement the HABTM relationship.
I am facing trouble implementing the HABTM relationship as there are many lessons per subjects and I am not sure how I could distinguish between the different lessons.
To elaborate further, I am able to get the nested form working but I can't get the HABTM checkboxes to save to the right lessons.
The following code sample is my HABTM checkbox implementation.
<% Group.all.each do |group|%>
<%= check_box_tag "subject[lessons_attributes[0]][group_ids][]", group.id, f.object.groups.include?(group) %>
<%= group.group_index %>
<%end%>
Currently, I have saved it to the first lesson using this line "subject[lessons_attributes[0]][group_ids][]".
However, the number of lessons vary and I am not too sure how I could determine the lesson "number", i.e. the bolded 0 in "subject[lessons_attributes[0]][group_ids][]". Such that I could save the groups to the correct lesson.
Any advice would be appreciated.
the best practice is to prebuild (a few) lesson object on the subject (that is the form.object), then you iterate over them to have per-lesson fields.
if you use simple_form or formtastic, collection select via checkboxes is easy:
<% form_for @subject do |form| %>
....
<% form.fields_for :lessons do |lesson_form| %>
...
<% lesson_form.input :group_ids, :as => :check_boxes %>
if you wanna use check_box_tag
, you should iterate through lessons with an index and substitute the index in your checkbox name:
<% form_for @subject do |form| %>
....
<% @subject.lessons.each_with_index do |l, i| %>
<% Group.all.each do |group|%>
<%= check_box_tag "subject[lessons_attributes[#{i}]][group_ids][]", group.id, l.groups.include?(group) %>
<%= group.group_index %>
<% end %>
For those working with Rails 4 and having the same question (as I had)
The Group.all.each
loop in @Viktor Trón's answer is unnecessary:
There's a new FormBuilder method, collection_check_boxes, which has been created just for that!
Your code would be:
<% form_for @subject do |form| %>
....
<% @subject.lessons.each_with_index do |l, i| %>
<%= form.fields_for :lessons, l do |lesson_fields|%>
<%= lesson_fields.collection_check_boxes :group_ids, Group.all, :id, :group_index %>
<% end %>
<% end %>
<% end %>
You then have to add accepts_nested_attributes_for :lessons
to your Subject
model, and in your SubjectsController
, change the subject_params
method to "permit" nested params for lessons:
params.require(:subject).permit(..., lessons_attributes: [:id, group_ids: []])
In your SubjectsController
, the create
or update
actions remain unchanged: @subject = Subject.create(subject_params)
for instance, will create the Subject, the associated lessons and update their HABTM relationships to groups correctly (unless I made a mistake somewhere!).