问题
I've been asking iterations of this question for a while now - but with a lot of help from the SO community i've very nearly got it working. So first, thanks!
I have a form to create a new student_group
and am using the Cocoon
gem:
<%= form_for @student_group do |f| %>
<p>
The name of this group is
<span class="field form_field"><%= f.text_field :name %></span>
and it is a/an
<span class="field dropdown"><%= f.select :type_of_group, [["select a group type", ""], "young learners class (0-6)", "primary class (7-12)", "secondary class (13-17)", "adult class (18+)", "children's sport team", "adult's sport team"] %></span>
</p>
<p>
There are
<span id="nos" class="field dropdown"><%= f.select :number_of_students, (0..60) %></span>
students in this group.
</p>
<table id="students_form_table">
<tbody>
<!-- https://stackoverflow.com/questions/11445831/how-to-submit-multiple-new-items-via-rails-3-2-mass-assignment -->
<%= f.fields_for :students, @student, child_index: @student do |builder| %>
<%= render "student_fields", :f => builder %>
<% end %>
</tbody>
</table>
<p>
<%= f.submit "Submit", :class => 'big_button round unselectable'%>
</p>
<% end %>
and the 'student_fields' partial:
<tr class="nested-fields">
<td id="student_name" class="field form_field"><%= f.text_field :name, placeholder: "Student name" %></td>
<td id="student_gender" class="field dropdown"><%= f.select :gender, [["select a gender", ""],'Female', 'Male', 'Transgender'] %></td>
<td id="remove_link"><%= link_to_remove_association "remove student", f %></td>
</tr>
Now the heart of the question:
I have the following .js file to dynamically add the correct number of student fields to the form:
var row_i = 0;
function emptyRow() {
row_i++;
this.obj = $("<tr></tr>");
this.obj.append('<td id="student_name" class="field form_field"><input id="student_group_students_attributes___Student:0x00000103c4ead8__name" name="student_group[students_attributes][#<Student:0x00000103c4ead8>][name]" type="text" /></td>');
this.obj.append('<td id="student_gender" class="field dropdown"><select id="student_group_students_attributes___Student:0x00000103c4ead8__gender" name="student_group[students_attributes][#<Student:0x00000103c4ead8>][gender]"><option value="">select a gender</option><option value="Female">Female</option><option value="Male">Male</option><option value="Transgender">Transgender</option></select></td>');
this.obj.append(' <td id="remove_link"><input id="student_group_students_attributes___Student:0x00000103c4ead8___destroy" name="student_group[students_attributes][#<Student:0x00000103c4ead8>][_destroy]" type="hidden" /><a href="#" class="remove_fields dynamic">remove student</a></td>')
}
function refresh(new_count) {
//how many students we have got?
console.log("New count= " + new_count);
if (new_count > 0) {
$('#students_form_table').show();
}
else {
$('#students_form_table').hide();
}
var old_count = parseInt($('tbody').children().length);
console.log("Old count= " + old_count);
//calculates difference between rows needed/rows current
var rows_difference = parseInt(new_count) - old_count;
console.log("Rows diff= " + rows_difference);
//if we have rows to add
if (rows_difference > 0) {
for (var i = 0; i < rows_difference; i++)
$('tbody').append((new emptyRow()).obj);
} else if (rows_difference < 0) //we need to remove rows ..
{
var index_start = old_count + rows_difference + 1;
console.log("Index start= " + index_start);
$('tr:gt(' + index_start + ')').remove();
row_i += rows_difference;
}
}
$(document).ready(function () {
//hide table by default
$('#students_form_table').hide();
$('#nos').change(function () {
var opt=$('#nos option:selected');
refresh(opt.text());
})
});
So this form very nearly works - everything formats correctly, and when I select 'x' number of students, 'x' number of student forms are built - however, because of this -
function emptyRow() {
row_i++;
this.obj = $("<tr></tr>");
this.obj.append('<td id="student_name" class="field form_field"><input id="student_group_students_attributes___Student:0x00000103c4ead8__name" name="student_group[students_attributes][#<Student:0x00000103c4ead8>][name]" type="text" /></td>');
this.obj.append('<td id="student_gender" class="field dropdown"><select id="student_group_students_attributes___Student:0x00000103c4ead8__gender" name="student_group[students_attributes][#<Student:0x00000103c4ead8>][gender]"><option value="">select a gender</option><option value="Female">Female</option><option value="Male">Male</option><option value="Transgender">Transgender</option></select></td>');
this.obj.append(' <td id="remove_link"><input id="student_group_students_attributes___Student:0x00000103c4ead8___destroy" name="student_group[students_attributes][#<Student:0x00000103c4ead8>][_destroy]" type="hidden" /><a href="#" class="remove_fields dynamic">remove student</a></td>')
}
...the fields aren't being rendered correctly - in that they aren't creating discreet instances of new student fields. I understand that the problem is the hash in the id/name portion of the html generated by rails, but i'm not sure how to replace it so that this will work. the closest i've come is trying to adapt the coffeescript from this site, but I couldn't get it working.
So how to dynamically generate new forms for models using jquery?
EDIT: Actually I've just realized that the first and last forms will save properly - that is, if I add eight students, student1 and student8 will be entered into the database, but the others will not. this only confuses me more :/
EDIT 2: I've found this jquery api called 'serialize' in the context of this question and i think it might be what i need, but having trouble adapting it. Help me!
回答1:
Got it!
Tried out a lot of things but this is what worked in the end:
student_groups_controller.rb #create
def create
@params = params[:student_group][:students_attributes]
@student_group = @user.student_groups.build(params[:student_group])
if @student_group.save
### RE: 'defensive coding' http://stackoverflow.com/questions/14502508/undefined-method-for-nilnilclass-when-pushing-values-to-an-array
if @params.present?
### http://stackoverflow.com/questions/11355820/rails-3-2-iterate-through-an-array
@params.each do |student|
@student_group.students.create(name:"#{student[:name]}", gender: "#{student[:gender]}")
end
end
# new subject path
redirect_to class_path(@student_group), flash: { success: "#{@student_group.name} has been added successfully" }
else
@title = "Create a new group"
flash.now[:error] = "Something's gone wrong. Please try again!"
render 'new'
end
end
relevant section of _groups_form.html.rb
<table id="students_form_table">
<tbody>
<!-- http://stackoverflow.com/questions/11445831/how-to-submit-multiple-new-items-via-rails-3-2-mass-assignment -->
<%= fields_for 'student_group[students_attributes][]', @student, index: nil do |builder| %>
<%= render "student_fields", :f => builder %>
<% end %>
</tbody>
</table>
and finally, the relevant portion of student_groups.js
:
function emptyRow() {
row_i++;
this.obj = $("<tr></tr>");
this.obj.append('<td id="student_name" class="field form_field"><input id="student_group_students_attributes__name" name="student_group[students_attributes][][name]" placeholder="Student name" size="30" type="text" /></td>');
this.obj.append('<td id="student_gender" class="field dropdown"><select id="student_group_students_attributes__gender" name="student_group[students_attributes][][gender]"><option value="">select a gender</option><option value="Female">Female</option><option value="Male">Male</option><option value="Transgender">Transgender</option></select></td>');
this.obj.append('<td id="remove_link"><input id="student_group_students_attributes___destroy" name="student_group[students_attributes][][_destroy]" type="hidden" /><a href="#" class="remove_fields dynamic">remove student</a></td>')
}
来源:https://stackoverflow.com/questions/17527384/how-to-dynamically-add-nested-attributes-in-rails-with-jquery