该项目是一个简单的锻炼创建者,您可以在锻炼计划中添加锻炼。
我一直在关注Railscast覆盖嵌套模型表单以允许动态添加和删除练习,但遇到了错误,需要作为新开发人员的第二意见。
我不断收到的错误是:计划中的NoMethodError#show
这是提取的代码,带有加星标记的突出显示错误:
<fieldset>
**<%= link_to_add_fields "Add Exercise", f, :exercise %>**
<%= f.text_field :name %>
<%= f.number_field :weight %>
<%= f.number_field :reps %>
注意:我已经创建了锻炼模型,但没有锻炼控制器。练习只能存在于计划中,但我不确定我是否仍需要在练习控制器中创建动作来添加练习?
我几乎逐字地跟着Railscast(_exercise_fields部分我稍微偏离了)所以你能够查看我在文件中分享的文件。
我的schema.rb
create_table "exercises", force: true do |t|
t.string "name"
t.integer "weight"
t.integer "reps"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "plan_id"
end
create_table "plans", force: true do |t|
t.string "title"
t.datetime "created_at"
t.datetime "updated_at"
end
我的计划模型:
class Plan < ActiveRecord::Base
has_many :exercises
accepts_nested_attributes_for :exercises, allow_destroy: true
end
我的运动模型:
class Exercise < ActiveRecord::Base
belongs_to :plan
end
我的_form.html.erb
<%= form_for @plan do |f| %>
<% if @plan.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@plan.errors.count, "error") %> prohibited this plan from being saved:</h2>
<ul>
<% @plan.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<%= f.fields_for :exercises do |builder| %>
<%= render 'exercise_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Exercise", f, :exercises %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
我的_exercise_fields.html.erb
<fieldset>
<%= link_to_add_fields "Add Exercise", f, :exercise %>
<%= f.text_field :name %>
<%= f.number_field :weight %>
<%= f.number_field :reps %>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</fieldset>
我的计划.js.coffee
jQuery ->
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
我的application_helper.rb
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
我对编程比较陌生,所以如果我容易忽视某些内容,我会提前道歉。我们非常感谢任何帮助,建议或潜在客户阅读我的问题。
谢谢!
答案 0 :(得分:1)
实施了您所寻求的功能后,我会提出一些想法:
如您所知,您可以使用accepts_nested_attributes_for
函数将属性从父级传递到嵌套模型
虽然相对简单,但它有一个学习曲线。所以我将在这里解释如何使用它:
#app/models/plan.rb
Class Plan < ActiveRecord::Base
has_many :exercises
accepts_nested_attributes_for :exercises, allow_destroy: true
end
如果正确显示,这会为plan
模型提供通过任何额外数据发送的“命令”
要正确发送数据(在Rails 4中),有几个重要步骤:
1. Build the ActiveRecord Object
2. Use `f.fields_for` To Display The Nested Fields
3. Handle The Data With Strong Params
构建ActiveRecord对象
#app/controllers/plans_controller.rb
def new
@plan = Plan.new
@plan.exericses.build
end
使用f.fields_for显示嵌套字段
#app/views/plans/new.html.erb
<%= form_for @plans do |f| %>
<%= f.fields_for :exercises do |builder| %>
<%= builder.text_field :example_field %>
<% end %>
<% end %>
处理具有强大参数的数据
#app/controllers/plans_controller.rb
def create
@plan = Plan.new(plans_params)
@plan.save
end
private
def plans_params
params.require(:plan).permit(:fields, exerices_attributes: [:extra_fields])
end
这应该将所需数据传递给嵌套模型。如果没有这个,您将不会传递数据,并且您的嵌套表单将无法正常工作
附加额外字段是棘手的部分
问题是,只能在f.fields_for
对象(仅存在于实例中)中生成新的form
对象
Ryan Bates通过将当前form
对象发送给帮助程序来解决这个问题,但这会导致问题,因为帮助程序会将新字段的完整源代码附加到链接“on click
中事件(效率低下)
我们发现this tutorial更适合
它的工作原理如下:
f.fields_for
&amp; form
部分(对于ajax)创建2个部分
#app/views/plans/add_exercise.html.erb
<%= form_for @plan, :url => plans_path, :authenticity_token => false do |f| %>
<%= render :partial => "plans/exercises_fields", locals: {f: f, child_index: Time.now.to_i} %>
<% end %>
#app/views/plans/_exercise_fields.html.erb
<%= f.fields_for :exercises, :child_index => child_index do |builder| %>
<%= builder.text_field :example %>
<% end %>
创建新路线
#config/routes.rb
resources :plans do
collection do
get :add_exercise
end
end
创建控制器操作
#app/controllers/plans_controller.rb
def add_exercise
@plan = Plan.new
@plan.exercises.build
render "add_exericse", :layout => false
end
创建JS以添加额外字段
#app/assets/javascripts/plans.js.coffee
$ ->
$(document).on "click", "#add_exercise", (e) ->
e.preventDefault();
#Ajax
$.ajax
url: '/messages/add_exercise'
success: (data) ->
el_to_add = $(data).html()
$('#exercises').append(el_to_add)
error: (data) ->
alert "Sorry, There Was An Error!"
为庞大的职位道歉,但它应该工作&amp;帮助显示更多信息