一张表格上有多条记录

时间:2015-04-29 12:27:32

标签: ruby-on-rails

我想创建一个具有以下格式的表单:

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”操作中将它们分配给子级。

3 个答案:

答案 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

这应该呈现一个将被解析为单个哈希的字段集。但您可能需要一组嵌套的字段集,请参阅文档。

最终你会得到:

  • 哈希
    • 共享参数
    • 对象数组
    • 对象A
    • 对象A
    • 对象B
    • ...

现在是最终的问题 - 您需要通过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 -%>
相关问题