【问题标题】:How to Create Nested Forms in Rails 4如何在 Rails 4 中创建嵌套表单
【发布时间】:2014-08-14 23:13:37
【问题描述】:

我试图让用户通过他们各自的连接表(锻炼设备,锻炼肌肉)在多对多关系中创建带有设备和肌肉的练习。我已经获得了用于在每次锻炼中添加一个设备/肌肉的表单,但无法弄清楚如何添加链接以动态添加另一个字段。

我查看了RailsCaststhis post,并在previous post of my own 上将其作为附带问题提出,但根本无法使用此功能。我对 Rails 4 还很陌生,并且仍在尝试学习 Javascript,但我希望能详细说明如何以 Rails 4 的方式设置它!

我的模特:

# id        :integer
# name      :string
# is_public :boolean
Exercise
    has_many :exercise_equipment
    has_many :equipment, :through => :exercise_equipment
    accepts_nested_attributes_for :exercise_equipment


# id           :integer
# exercise_id  :integer
# equipment_id :integer
# optional     :boolean
ExerciseEquipment
    belongs_to :exercise
    belongs_to :equipment
    accepts_nested_attributes_for :equipment


# id   :integer
# name :string
Equipment
    has_many :exercise_equipment
    has_many :exercises, :through => :exercise_equipment

我的控制器方法:

def new
  @exercise = Exercise.new
  @exercise.exercise_equipment.build
  @exercise.exercise_muscles.build
end

def create
  exercise = current_user.exercises.new( exercise_params )
  if exercise.save!
    redirect_to exercise
  else
    render 'new'
  end
end

views/exercises/new.html.erb

<h1>Create New Exercise</h1>

<%= form_for @exercise do |f| %>
  <%= render 'form', f: f %>
  <%= f.submit "New Exercise" %>
<% end %>

视图/练习/_form.html.erb

<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>

<%= f.check_box :is_public %> Public

<%= f.fields_for :exercise_muscles do |emf| %>
  <%= emf.collection_select :muscle_id, Muscle.all, :id, :name, { include_hidden: false } %>
<% end %>

<%= f.fields_for :exercise_equipment do |eef| %>
  <%= eef.collection_select :equipment_id, Equipment.all, :id, :name, { include_hidden: false } %>
  <%= eef.check_box :optional %> Optional
<% end %>

【问题讨论】:

    标签: javascript ruby-on-rails ruby ruby-on-rails-4


    【解决方案1】:

    Ajax

    无法弄清楚如何添加链接以将另一个字段添加到表单 在飞行中

    这样做的“Rails 方式”是从ajax 请求中“拉”出fields_for 块的新实例。

    推荐 Ajax 的原因是因为它是“Rails 方式”——完全模块化和可扩展:

    #config/routes.rb
    resources :exercises do
       get :add_field, on: :collection
    end
    
    #app/models/exercise.rb
    Class Exercise < ActiveRecord::Base
       ...
       def self.build #-> allows you to call a single method
          exercise = self.new
          exercise.exercise_equipment.build
          exercise.exercise_muscles.build
          return
       end
    end
    
    #app/controllers/exercises_controller.rb
    Class ExercisesController < ApplicationController
       def add_field
          @exercise = Exercise.build
          respond_to do |format|
             format.html
             format.js
          end
       end
    end
    
    #app/views/exercises/new.html.erb
    <%= form_for @exercise do |f| %>
       <%= render "fields", locals: { f: f } %>
       <%= f.submit %>
    <% end %>
    
    #app/views/exercises/add_field.js.erb
    $("#form_element").append("<%=j render "exercises/form", locals: { exercise: @exercise } %>");
    
    #app/views/exercises/_form.html.erb
    <%= form_for exercise do |f| %>
       <%= render "fields", locals: { f: f } %>
    <% end %>
    
    #app/views/exercises/_fields.html.erb
    <%= f.fields_for :exercise_muscles, child_index: Time.now.to_i do |emf| %>
        <%= emf.collection_select :muscle_id, Muscle.all, :id, :name, { include_hidden: false } %>
    <% end %>
    <%= link_to "New Field", exercises_add_fields_path, remote: :true %>
    
    <%= f.fields_for :exercise_equipment, child_index: Time.now.to_i do |eef| %>
       <%= eef.collection_select :equipment_id, Equipment.all, :id, :name, { include_hidden: false } %>
       <%= eef.check_box :optional %> Optional
    <% end %>
    <%= link_to "New Field", exercises_add_fields_path, remote: :true %>
    

    这将使您能够通过 ajax 调用创建字段;这是正确的做法

    【讨论】:

    • 我想我不明白“#form_element”的来源。我需要将它作为一个类添加到我的 fields_for 中吗?
    • 另外,这些link_to 不会在每个表单中添加一个,而不是添加新的肌肉或设备字段吗?一个有效的用例是锻炼 5 块肌肉但不使用任何设备。
    • 好的,是的,你是对的。我打算添加部分区分两者的能力 - 你想让我更新吗?
    • 我想我知道你只是通过两条不同的路径来做到这一点,但如果你能更新你的答案让我检查一下,那就太好了。并且也许添加声明#form_element 的位置?
    • 您必须在原始视图中声明 - 您希望将字段附加到 &lt;form&gt;
    【解决方案2】:

    在尝试使用 Rich 的解决方案后,我想找到一个在所用代码方面更简洁的解决方案。我找到了 gem Cocoon,它非常好用,而且集成起来非常简单。

    我的主要 _form 视图:

    <div class="form-group">
      <%= f.label :name %><br />
      <%= f.text_field :name, autofocus: true %>
    </div>
    
    <div class="form-group">
      <%= f.check_box :is_public %> Public
    </div>
    
    <div class="form-group">
      <%= f.fields_for :exercise_muscles do |emf| %>
        <%= render 'exercise_muscle_fields', :f => emf %>
      <% end %>
      <%= link_to_add_association 'Add Muscle', f, :exercise_muscles %>
    </div>
    
    <div class="form-group">
      <%= f.fields_for :exercise_equipment do |eef| %>
        <%= render 'exercise_equipment_fields', :f => eef %>
      <% end %>
      <%= link_to_add_association 'Add Equipment', f, :exercise_equipment %>
    </div>
    

    从这里可以看出,添加一个简单的“link_to_add_association”方法会在后台处理所有的 Javascript。对于未来的读者,以下是每个表单组包含的部分:

    _exercise_muscle_fields:

    &lt;%= f.collection_select :muscle_id, Muscle.all.order( 'muscle_group_id ASC' ), :id, :name, { include_hidden: false } %&gt;

    _exercise_equipment_fields:

    <%= f.collection_select :equipment_id, Equipment.all.order( 'name ASC' ), :id, :name, { include_hidden: false } %>
    <%= f.check_box :optional %> Optional
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多