我想创建一个具有以下格式的表单:
Shared Fields
Fields Unique to record A
[add another record button]
可以根据需要多次单击[添加]按钮以创建
Shared Fields
Fields Unique to record A
Fields Unique to record B
Fields Unique to record C
[add another record button]
提交表单会创建记录A,B和C
后端
这将创建3条记录,每条记录都根据表单中的字段具有共享和唯一属性。
更新
分享的一件事是他们每个人都属于“雇主”。那么这应该是一个form_for @employer?
大声思考,我们可以将共享属性保存为父级的attr_accessors,并在子级控制器的“child#create”操作中将它们分配给子级。
答案 0 :(得分:2)
由于您在没有嵌套的情况下在一个表单中填写多个模型,因此您不能仅使用form_for @something
生成表单,因为您没有单个对象可以填写。
我会检查数据库结构,看看是否有办法提取单独模型的共享字段。但如果没有办法干净利落,请继续阅读。
用于呈现对象表单的Rails助手以Rails可以正确解析为哈希的方式布局字段。反过来,它可以用于构造对象。通过使用:
form_for @thing do |f|
t.text_field :name
end
你会得到一个包含thing[name]
字段的表单,它会像params
那样查看:
{..., thing: {name: "Hello world"}, ...}
有关详细信息,请参阅the guides。
现在这里是catch :你没有一个对象可以填写。但你可以忽略它并构建一个仍然被解析为嵌套哈希的表单。您只需要手动填写Rails猜测的内容:表单URL和对象名称。
form_for :thing, url: thing_path do |f|
f.text_field :name
end
如果要创建一组字段,请在此表单中使用fields_for
帮助程序。更多信息in the documentation,其形式可能如下所示:
form_for :thing, url: thing_url do |f|
f.text_field :name
f.fields_for :something do |s|
s.text_field :value
end
end
这应该呈现一个将被解析为单个哈希的字段集。但您可能需要一组嵌套的字段集,请参阅文档。
最终你会得到:
现在是最终的问题 - 您需要通过JavaScript向表单添加字段,但它应该事先知道要添加的标记。有很多方法可以解决这个问题,一种方法是为每个独特的模型渲染一个示例表单并使用JavaScript对其进行采样。
编辑:巧合的是,我需要了解如何渲染一个可以由JavaScript复制的“裸”(没有被对象支持)表单,并且看起来像参数中的对象数组当给出多种形式时。
棘手的部分是,如果您使用fields_for :something
,它将为单个对象生成一个表单,而不是它们的数组。四处寻找,我发现了一个看似无证的功能(从this post挖掘代码)。它是这样使用的(为简洁起见,我使用HAML / Slim语法):
= form_for :thing, url: whatever do |f|
= f.fields_for :stuff, index: '' do |s| #<< the `index: ''` part
= s.text_field :v
从语义上看,它似乎意味着以下内容:制作一个包含字段stuff
的表单,该表单由一个或多个具有空索引的字段集填充。在引擎盖下,它乍一看会产生一些尴尬的字段名称:
thing[stuff][v] # Before, without ` index: '' `
thing[stuff][][v] # After, see the empty index?
这个有趣的部分是你可以克隆resultig字段集而不修改它,Rails(甚至是Rack?)会将这组表单解析成一个单独的对象。这取决于浏览器保留字段的顺序,这在大多数情况下都是正确的。
答案 1 :(得分:0)
在你的new.html.erb
中<div id="formDiv">
<%= form_for @user do |f| %>
<%= render 'shared/error_messages' %><br>
<%= f.label :name %><br>
<%= f.text_field :name %><br>
<%= f.label :email %><br>
<%= f.email_field :email %><br>
<%= f.label :password %><br>
<%= f.password_field :password %><br>
<%= f.label :password_confirmation, "Confirmation" %><br>
<%= f.password_field :password_confirmation %><br>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
<input id="add" type="submit" value="Add"/>
</div>
<script>
$("#add").click(function() {
$.ajax({
url: "newAjax",
success: function (html) {
$("#formDiv").append(html);
}
});
});
$('form').submit(function(){
$('.multiform').each(function() {
$(this).submit();
});
return true;
});
</script>
每次单击按钮时,都会使用AJAX添加新表单,当您尝试提交表单时,它会循环显示其他表单,并使用“multiform”类并首先提交它们。
在你的newAjax.html.erb
中<%= form_for @user, remote: true, html: { class: 'multiForm' } do |f| %>
<%= f.label :name %><br>
<%= f.text_field :name %><br>
<%= f.label :email %><br>
<%= f.email_field :email %><br>
<%= f.label :password %><br>
<%= f.password_field :password %><br>
<%= f.label :password_confirmation, "Confirmation" %><br>
<%= f.password_field :password_confirmation %><br>
<% end %>
我们在这些表单中使用AJAX,因此无需重定向即可提交。 最后在你的控制器中
def newAjax
@user = User.new
render :layout => false
end
我们希望确保我们的布局不会使用AJAX表单呈现,因此我们不会重新加载您的JS / CSS文件等。
答案 2 :(得分:0)
这是我最后使用的工作代码。感谢所有帮助过的人。遵循D-side的方法,但使用“雇主”作为基本模型。我可能会重构这个以删除Employer上的attr_accessors,而是在视图中使用字段标记。最后,我只是从传递给控制器的“params”数组中取出共享值。
预订控制器:
def new
@employer = current_employer
@booking = @employer.bookings.build
respond_with(@booking)
end
def create
errors = []
booking_create_params["new_booking_attributes"].each do |booking|
new_booking = current_employer.bookings.build(booking)
new_booking.job_type_id = booking_create_params["job_type_id"]
new_booking.vehicle_id = booking_create_params["vehicle_id"]
new_booking.location_id = booking_create_params["location_id"]
new_booking.pay = booking_create_params["pay"]
unless new_booking.save
errors << new_booking.errors.full_messages
end
end
if errors == []
flash[:notice] = "Bookings posted!"
redirect_to new_booking_path
else
flash[:notice] = "Error: #{errors.join(', ')}!"
redirect_to new_booking_path
end
end
在视图中:
<div id="bookings">
<ol class="numbers">
<li>
<legend>Location, Pay, & Vehicle</legend>
<div class="form-group">
<div class="row">
<div class="col-sm-6">
<label>Type of job</label><br>
<%= f.select(:job_type_id, options_from_collection_for_select(JobType.all, :id, :name_with_delivery), {}, { id: 'job-type', class: 'form-control' }) %>
</div>
<div class="col-sm-6">
<label>Vehicle needed</label><br>
<%= f.select(:vehicle_id, options_from_collection_for_select(Vehicle.all, :id, :name), {}, { id: 'vehicle-type', class: 'form-control' }) %>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-sm-6">
<label>Location</label>
<% if current_employer.locations.count > 1 %>
<%= f.select :location_id, options_from_collection_for_select(current_employer.locations.all, :id, :name_or_address_1), {}, { class: 'form-control' } %>
<% elsif current_employer.locations.count == 1 %>
<p><strong>Location: </strong><%= current_employer.locations.first.name_or_address_1 %></p>
<%= f.hidden_field :location_id, current_employer.locations.first.id %>
<% end %>
<%= link_to "or add new location", new_employer_location_path(current_employer, Location.new) %>
</div>
<div class="col-sm-6">
<%= f.label :pay %><br>
<%= f.text_field :pay, class: 'form-control' %>
</div>
</div>
</div>
</li>
<legend>Shifts</legend>
<%= render 'booking', booking: Booking.new %>
</ol>
</div>
部分是:
<li class="close-list-item">
<!-- Recommended: post at least 1 week in advance
& shift length at least 4 hours.
-->
<% new_or_existing = booking.new_record? ? 'new' : 'existing' %>
<% prefix = "employer[#{new_or_existing}_booking_attributes][]" %>
<%= fields_for prefix, booking do |booking_form| -%>
<div class="shifts">
<div class="form-group shift">
<div class="row">
<div class="col-sm-4">
<label for="">Date</label>
<i class="glyphicon glyphicon-time"></i>
<%= booking_form.text_field :start, class: 'form-control booking-date', placeholder: 'Date', data: { provide: "datepicker", date_clear_btn: "true", date_autoclose: "true", date_start_date: '+1d', date_format: "yyyy-mm-dd" } %>
</div>
<div class="col-sm-4">
<label>Time Start</label><br>
<%= booking_form.select(:start_time, options_for_select(
booking_times_array
), { include_blank: true }, { class: 'form-control booking-time booking-time-start' } ) %>
</div>
<div class="col-sm-4">
<label>
Time End
</label>
<%= link_to "javascript:;", class: 'pull-right remove-shift' do %>
<i class="glyphicon glyphicon-remove"></i>
<% end %>
<script type="text/javascript">
$(".remove-shift").click(function(){
$(this).parents("li").remove();
});
</script>
<%= booking_form.select(:end_time, options_for_select(
booking_times_array
), { include_blank: true }, { class: 'form-control booking-time booking-time-end' } ) %>
</div>
</div>
</div>
</div>
</li>
<% end -%>