Rails 形成对象与改革轨道与集合不起作用或验证
[英] Rails form object with reform-rails with collections not working or validating
本文介绍了Rails 形成对象与改革轨道与集合不起作用或验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我正在使用
现在我继续提交.应该发生的是应该出现验证错误,因为必须存在第二封电子邮件的值.但是,在此处提交时是日志:
参数:{"utf8"=>"✓", "authenticity_token"=>"123abc==", "user"=>{"name"=>"neil", "user_emails_attributes"=>{"0"=>{"email_text"=>"email_test1"}, "1"=>{"email_text"=>""}}}, "commit"=>"创建用户"}ActiveModel::UnknownAttributeError(用户的未知属性user_emails_attributes".):
所以我的表单对象存在一些问题.
我怎样才能让这个表单对象工作?是否可以使用 reform_rails
并使用 accepts_nested_attributes
使这个表单对象工作?最终:我只想让表单对象工作.
除了reform-rails 文档之外,我已经探索了一些资源:
我第一次尝试制作表单对象是使用 virtus gem,但我似乎无法让那个工作要么.我也为该实现发布了一个 stackoverflow 问题.
解决方案
完整答案:
模型:
# app/models/user.rb类用户<申请记录has_many :user_emails结尾# app/models/user_email.rb类 UserEmail <申请记录归属地:用户结尾
表单对象:
# app/forms/user_form.rb# 如果使用最新版本的改革(2.2.4):您现在可以在属性上调用验证类用户窗体 <改革::形式属性:名称,验证:{存在:真}收集:user_emails 做属性:email_text,验证:{存在:真}结尾结尾
控制器:
# app/controllers/users_controller.rb类用户控制器 <应用控制器before_action :user_form, only: [:new, :create]定义新结尾# 验证方法实际上来自改革,这将把你的参数持久化到类对象# 您添加到 UserForm 对象中.# 这也将根据您的用户表单是否有效返回布尔值 true 或 false.# 您可以传递 params[:user][:user_emails] 或 params[:user][user_email_attributes].# 改革很聪明,可以兼顾两者.# 我不确定你是否需要使用强参数,但你可以.定义创建如果@user_form.validate(user_params)@user_form.saveredirect_to users_path,注意:用户已成功创建."别的渲染:新结尾结尾私人的# 在钩子中调用这个方法,这样你就不必重复了定义用户表单user = User.new(user_emails: [UserEmail.new, UserEmail.new])@user_form ||= UserForm.new(user)结尾# 无需在 user_emails_attributes 中添加 :id定义用户参数params.require(:user).permit(:name, user_emails_attributes: [:_destroy, :email_text])结尾结尾
表格:
# app/views/users/new.html.erb<h1>新用户</h1><%= render 'form', user_form: @user_form %><%= link_to '返回', users_path %>
<小时>
#app/views/users/_form.html.erb<%= form_for(user_form, url: users_path) 做 |f|%><% if user_form.errors.any?%><div id="error_explanation"><h2><%=pluralize(user_form.errors.count, "error") %>禁止保存此用户:</h2><ul><% user_form.errors.full_messages.each do |message|%><li><%=消息%></li><%结束%>
<%结束%><div class="field"><%= f.label :name %><%= f.text_field :name %>
<%= f.fields_for :user_emails 做 |email_form|%><div class="field"><%= email_form.label :email_text %><%=email_form.text_field :email_text %>
<%结束%><div class="actions"><%= f.submit %>
<%结束%>
I am using the reform-rails gem In order to utilize a form object in my rails project.
I realize a form object is probably overkill for the example code I use below, but it is for demonstration purposes.
In the form I am creating a user
, and associated to that user record are two user_emails
.
# models/user.rb
class User < ApplicationRecord
has_many :user_emails
end
# models/user_email.rb
class UserEmail < ApplicationRecord
belongs_to :user
end
Notice that I am not using accepts_nested_attributes_for :user_emails
within the User
model. It appears to me that one of the main points of form objects is that it helps you get away from using accepts_nested_attributes_for
, so that is why I am attempting to do this without it. I got that idea from this video which talks about refactoring fat models. I have the link pointing to the section of the video on form objects, and he expresses how much he dislikes accepts_nested_attributes_for
.
I then proceed to create my 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
So the user_form
object wraps a user
record and then a couple of user_email
records associated to that user
record. There are form-level validations on the user
and on the user_email
records this form wraps:
- the
user#name
must have a value
- each
user_email#email_text
must have a value
If the form is valid: then it should create one user
record and then a couple of associated user_email
records. If the form is not valid: then it should re-render the form with error messages.
I will show what I have in the controller thus far. For brevity: only displaying the new
action and the create
action:
# 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
Lastly: the form itself:
# 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 %>
As a test: here is the form with inputted values:
Now I proceed to submit. What should happen is there should be a validation error because a value for that second email must be present. However, when submitted here are the logs:
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.):
So there is some issue with my form object.
How can I get this form object to work? Is it possible to use reform_rails
and get this form object to work without using accepts_nested_attributes
? Ultimately: I just want to get the form objet to work.
Some resource I have already explored in addition to the reform-rails docs:
My first attempt to make a form object was with the virtus gem, but I could not seem to get that one working either. I did post a stackoverflow question for that implementation as well.
解决方案
Complete Answer:
Models:
# app/models/user.rb
class User < ApplicationRecord
has_many :user_emails
end
# app/models/user_email.rb
class UserEmail < ApplicationRecord
belongs_to :user
end
Form Object:
# 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
Controller:
# 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
The Form:
# 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 %>
这篇关于Rails 形成对象与改革轨道与集合不起作用或验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!