Рельсы формируют объект с помощью реформ-рельсов с не работающими или проверяемыми коллекциями.

Я использую камень реформ-рельсов, чтобы использовать объект формы в моем проекте рельсов.

Я понимаю, что объект формы, вероятно, излишний для примера кода, который я использую ниже, но это для демонстрационных целей.

В форме я создаю 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

Это будет проверять вашу модель пользователя как хорошо, как любые вложенные ассоциации.

Вот оно! Сухие модели, проверки и сохранение модели и ассоциаций.

Надеюсь, это поможет!

Другие вопросы по тегам