Рельсы формируют объект с помощью реформ-рельсов с не работающими или проверяемыми коллекциями.
Я использую камень реформ-рельсов, чтобы использовать объект формы в моем проекте рельсов.
Я понимаю, что объект формы, вероятно, излишний для примера кода, который я использую ниже, но это для демонстрационных целей.
В форме я создаю 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
Вот почему я пытаюсь сделать это без него. Я получил эту идею из этого видео, в котором говорится о рефакторинге толстых моделей. У меня есть ссылка, указывающая на раздел видео об объектах формы, и он выражает, насколько он не любит 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
? В конечном итоге: я просто хочу, чтобы форма работала.
Некоторый ресурс, который я уже исследовал в дополнение к документам "Рельсы реформ":
Моя первая попытка создать объект формы была с помощью драгоценного камня virtus, но я не мог заставить его работать. Я также опубликовал вопрос стека переполнения для этой реализации.
2 ответа
Полный ответ:
Модели:
# 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 %>
Я наконец получил это работающее!!!
Во-первых, я не смог получить его, сохранив коллекцию на Rails 5. Я создал 4.2.6, и она работает по-нашему. Я предлагаю вам создать проблему на странице репозитория github гема Reform.
Итак, это рабочий код:
модели / 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
Это будет проверять вашу модель пользователя как хорошо, как любые вложенные ассоциации.
Вот оно! Сухие модели, проверки и сохранение модели и ассоциаций.
Надеюсь, это поможет!