我正在使用reform-rails gem为了在rails项目中使用表单对象。
我意识到表单对象可能对我在下面使用的示例代码有些过分,但它是出于演示目的。
在我创建user
的表单中,与该用户记录关联的是两个user_emails
。
# models/user.rb
class User < ApplicationRecord
has_many :user_emails
end
# models/user_email.rb
class UserEmail < ApplicationRecord
belongs_to :user
end
请注意,我未在accepts_nested_attributes_for :user_emails
模型中使用User
。在我看来,表单对象的一个要点是它可以帮助你摆脱使用accepts_nested_attributes_for
,所以这就是为什么我试图在没有它的情况下做到这一点。我从this video那里得到了一个关于重构胖模型的想法。我有链接指向表单对象的视频部分,他表达了他不喜欢的accepts_nested_attributes_for
。
然后我继续创建我的user_form
:
# app/forms/user_form.rb
class UserForm < Reform::Form
property :name
validates :name, presence: true
collection :user_emails do
property :email_text
validates :email_text, presence: true
end
end
因此user_form
对象包含user
条记录,然后包含与该user_email
条记录相关联的几条user
条记录。 user
和此表单包含的user_email
记录中有 表单级验证 :
user#name
必须有值user_email#email_text
必须有一个值如果表单有效:那么它应该创建一个user
记录,然后创建几个关联的user_email
记录。如果表单无效:那么它应该重新呈现带有错误消息的表单。
到目前为止,我将展示控制器中的内容。为简洁起见:仅显示new
操作和create
操作:
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
user = User.new
user.user_emails.build
user.user_emails.build
@user_form = UserForm.new(user)
end
def create
@user_form = UserForm.new(User.new(user_params))
if @user_form.valid?
@user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_params
params.require(:user).permit(:name, user_emails_attributes: [:_destroy, :id, :email_text])
end
end
最后:表单本身:
# app/views/users/_form.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>
<% f.fields_for :user_emails do |email_form| %>
<div class="field">
<%= email_form.label :email_text %>
<%= email_form.text_field :email_text %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
作为测试:这是输入值的表单:
现在我继续提交。应该发生的是应该存在验证错误,因为必须存在第二封电子邮件的值。但是,这里提交的是日志:
Parameters: {"utf8"=>"✓", "authenticity_token"=>”123abc==", "user"=>{"name"=>"neil", "user_emails_attributes"=>{"0"=>{"email_text"=>"email_test1"}, "1"=>{"email_text"=>""}}}, "commit"=>"Create User"}
ActiveModel::UnknownAttributeError (unknown attribute 'user_emails_attributes' for User.):
因此我的表单对象存在一些问题。
如何让这个表单对象起作用?是否可以使用reform_rails
并使用accepts_nested_attributes
使此表单对象无需工作?最终:我只想让表格objet工作。
除了改革轨道文档之外,我已经探讨了一些资源:
我第一次尝试制作一个表单对象是virtus gem,但我似乎无法让那个工作。我也为该实现发布了stackoverflow question。
答案 0 :(得分:6)
完整答案:
<强>型号:强>
# app/models/user.rb
class User < ApplicationRecord
has_many :user_emails
end
# app/models/user_email.rb
class UserEmail < ApplicationRecord
belongs_to :user
end
表单对象:
# app/forms/user_form.rb
# if using the latest version of reform (2.2.4): you can now call validates on property
class UserForm < Reform::Form
property :name, validates: {presence: true}
collection :user_emails do
property :email_text, validates: {presence: true}
end
end
<强>控制器:强>
# app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :user_form, only: [:new, :create]
def new
end
# validate method actually comes from reform this will persist your params to the Class objects
# you added to the UserForm object.
# this will also return a boolean true or false based on if your UserForm is valid.
# you can pass either params[:user][:user_emails] or params[:user][user_email_attributes].
# Reform is smart enough to pick up on both.
# I'm not sure you need to use strong parameters but you can.
def create
if @user_form.validate(user_params)
@user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
private
# call this method in a hook so you don't have to repeat
def user_form
user = User.new(user_emails: [UserEmail.new, UserEmail.new])
@user_form ||= UserForm.new(user)
end
# no need to add :id in user_emails_attributes
def user_params
params.require(:user).permit(:name, user_emails_attributes: [:_destroy, :email_text])
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>
<%= f.fields_for :user_emails do |email_form| %>
<div class="field">
<%= email_form.label :email_text %>
<%= email_form.text_field :email_text %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
答案 1 :(得分:1)
我终于开始工作了!!!
首先,我无法在Rails 5上保存集合。我创建了一个4.2.6并且它可以使用我们的盒子。我建议你在Reform gem的github存储库页面上创建一个问题。
所以,这是工作代码:
模型/ user.rb
class User < ActiveRecord::Base
has_many :user_emails
end
模型/ user_email.rb
class UserEmail < ActiveRecord::Base
belongs_to :user
end
user_form.rb
class UserForm < Reform::Form
property :name
validates :name, presence: true
collection :user_emails, populate_if_empty: UserEmail do
property :email_text
validates :email_text, presence: true
end
end
验证发生时,populate_if_empty非常重要。
控制器创建方法:
def create
@user_form = UserForm.new(User.new)
if @user_form.validate(user_params)
@user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
这将validates您的用户模型以及任何嵌套关联。
你有它!干模型,验证和保存模型和关联。
我希望这有帮助!