【发布时间】:2017-03-21 14:34:44
【问题描述】:
下面的示例代码是一个人为的尝试使用表单对象的示例,在这种情况下使用表单对象可能是多余的。尽管如此:它显示了我遇到的问题:
我有两个模型:User 和 Email:
# app/models/user.rb
class User < ApplicationRecord
has_many :emails
end
# app/models/user.rb
class Email < ApplicationRecord
belongs_to :user
end
我想创建一个表单对象,它创建一个user 记录,然后创建三个关联的email 记录。
这是我的表单对象类:
# app/forms/user_form.rb
class UserForm
include ActiveModel::Model
attr_accessor :name, :email_forms
validates :name, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
puts "The Form is VALID!"
puts "I would proceed to create all the necessary objects by hand"
user = User.create(name: name)
email_forms.each do |email|
Email.create(user: user, email_text: email.email_text)
end
end
end
# app/forms/email_form.rb
class EmailForm
include ActiveModel::Model
attr_accessor :email_text, :user_id
validates :email_text, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
puts "The Form is VALID!"
# DON'T THINK I WOULD PERSIST DATA HERE
# INSTEAD DO IT IN THE user_form
end
end
注意:表单对象的验证。如果user_form 的name 属性为空,或者email_text 属性对于其email_form 数组中的任何email_form 对象为空,则认为user_form 无效。
为简洁起见:我将通过使用user_form 的new 和create 操作:
# app/controllers/user_controller.rb
class UsersController < ApplicationController
def new
@user_form = UserForm.new
@user_form.email_forms = [EmailForm.new, EmailForm.new, EmailForm.new]
end
def create
@user_form = UserForm.new(user_form_params)
if @user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_form_params
params.require(:user_form).permit(:name, {email_forms: [:_destroy, :id, :email_text, :user_id]})
end
end
最后:表单本身:
# app/views/users/new.html.erb
<h1>New User</h1>
<%= render 'form', user_form: @user_form %>
<%= link_to 'Back', users_path %>
# app/views/users/_form.html.erb
<%= form_for(user_form, url: users_path) do |f| %>
<% if user_form.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user_form.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user_form.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
# MESSY, but couldn't think of a better way to do this...
<% unique_index = 0 %>
<% user_form.email_forms.each do |email_form| %>
<div class="field">
<%= label_tag "user_form[email_forms][#{unique_index}][email_text]", "Email Text" %>
<%= text_field_tag "user_form[email_forms][#{unique_index}][email_text]" %>
</div>
<% unique_index += 1 %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
表单会渲染:
这是表单的html:
我去提交表格。这是参数哈希:
Parameters: {"utf8"=>"✓", "authenticity_token"=>”abc123==", "user_form"=>{"name"=>"neil", "email_forms"=>{"0"=>{"email_text"=>"test_email_1"}, "1"=>{"email_text"=>"test_email_2"}, "2"=>{"email_text"=>""}}}, "commit"=>"Create User form"}
应该发生的情况是应该重新渲染表单并且没有任何内容,因为 form_object 无效:所有三个关联的电子邮件不得为空。然而:form_object 认为它是有效的,它在UserForm 上的persist! 方法中爆炸了。它突出显示Email.create(user: user, email_text: email.email_text) 行并说:
["0", {"email_text"=>"test_email_1"}]:Array 的未定义方法 `email_text'
显然有几件事正在发生:嵌套验证似乎无法正常工作,并且我无法从 params 哈希重建每封电子邮件。
我已经检查过的资源:
- This Article 看起来很有希望,但我无法让它发挥作用。
- 我尝试使用 virtus gem 和reform-rails gem 来实现。我还针对这两个实现发布了未决问题:virtus attempt here,然后是reform-rails attempt here。
- 我尝试插入
accepts_nested_attributes,但无法弄清楚如何将其与表单对象以及嵌套表单对象一起使用(如本代码示例中所示)。部分问题在于has_many和accepts_nested_attributes_for似乎没有包含在ActiveModel::Model中。
非常感谢任何有关让此表单对象执行预期操作的指导!谢谢!
【问题讨论】:
-
你看过accepts_nested_attributes_for吗? api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/… ... 加上 gem
cocoon基本上消除了处理嵌套属性的所有艰苦工作。 -
@SteveTurczyn 是的,我看过
accepts_nested_attributes_for。我在弄清楚如何在表单对象中实现accepts_nested_attributes_for时遇到了很多麻烦。我更新了我的问题,以表达对该路线也进行了尝试。 -
说真的,看看'茧'。它使这一切变得容易。如果您在实现 'cocoon' 时遇到问题,请使用标签
ruby-on-rails和cocoon-gem发布问题,宝石就在这里...github.com/nathanvda/cocoon -
我遇到了同样的问题,用户模型有一个关联的模型地址。如果地址记录为空,如何设计formobject。如果地址记录为空,则应呈现地址字段,否则不应显示。
标签: ruby-on-rails ruby