Rails中的表单对象

下面的示例代码是对表单对象的尝试的一个人为设想的示例,其中使用表单对象可能是过度的。 尽管如此:它显示了我遇到的问题:

我有两个模型: UserEmail

 # 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 

注意表单对象validation 。 如果user_formname属性为空,或者如果email_text属性在其email_forms数组中的任何email_form对象中保留为空,则认为user_form无效。

为简洁起见:我将通过newcreate利用user_form

 # 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 

最后:forms本身:

 # app/views/users/new.html.erb 

New User


 # app/views/users/_form.html.erb   

prohibited this user from being saved:

# MESSY, but couldn't think of a better way to do this...

表单确实呈现:

被回报的形式的图片与输入的

这是表单的html:

表格的HTML

我去提交表格。 这是params哈希:

 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认为它是有效的,它会在persist!情况下爆炸persist! UserForm上的方法。 它突出显示了Email.create(user: user, email_text: email.email_text)行并说:

未定义方法`email_text’for [“0”,{“email_text”=>“test_email_1”}]:数组

很明显,有几件事情发生了:嵌套validation似乎无法正常工作,我无法从params散列重建每封电子邮件。

我已经检查过的资源:

  • 这篇文章似乎很有希望,但我无法让它工作。
  • 我尝试使用virtus gem和reform-rails gem实现。 我也为这两个实现发布了待处理的问题: virtus尝试在这里然后改革rails尝试在这里 。
  • 我试图插入accepts_nested_attributes ,但是无法弄清楚如何利用表单对象以及嵌套表单对象(如此代码示例中)。 部分问题是has_manyaccepts_nested_attributes_for似乎没有包含在ActiveModel::Model

有关获取此表单对象以执行预期操作的任何指导将非常感谢! 谢谢!

完整答案

楷模:

 #app/models/user.rb class User < ApplicationRecord has_many :emails end #app/models/email.rb class Email < ApplicationRecord belongs_to :user end 

控制器:

 #app/controllers/users_controller.rb class UsersController < ApplicationController def index @users = User.all end def new @user_form = UserForm.new @user_form.emails = [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, {emails_attributes: [:email_text]}) end end 

表格对象:

 #app/forms/user_form.rb class UserForm include ActiveModel::Model attr_accessor :name, :emails validates :name, presence: true validate :all_emails_valid def emails_attributes=(attributes) @emails ||= [] attributes.each do |_int, email_params| email = EmailForm.new(email_params) @emails.push(email) end end def save if valid? persist! true else false end end private def persist! user = User.new(name: name) new_emails = emails.map do |email_form| Email.new(email_text: email_form.email_text) end user.emails = new_emails user.save! end def all_emails_valid emails.each do |email_form| errors.add(:base, "Email Must Be Present") unless email_form.valid? end throw(:abort) if errors.any? end end app/forms/email_form.rb class EmailForm include ActiveModel::Model attr_accessor :email_text, :user_id validates :email_text, presence: true end 

浏览次数:

 app/views/users/new.html.erb 

New User

<%= 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? %>

<%= pluralize(user_form.errors.count, "error") %> prohibited this User from being saved:

    <% user_form.errors.full_messages.each do |message| %>
  • <%= message %>
  • <% end %>
<% end %>
<%= f.label :name %> <%= f.text_field :name %>
<%= f.fields_for :emails do |email_form| %>
<%= email_form.label :email_text %> <%= email_form.text_field :email_text %>
<% end %>
<%= f.submit %>
<% end %>