Rails Form Object with Virtus: has_many association

前端 未结 3 887
萌比男神i
萌比男神i 2020-12-17 06:24

I am having a tough time figuring out how to make a form_object that creates multiple associated objects for a has_many association with the virtus gem.

3条回答
  •  时光取名叫无心
    2020-12-17 07:00

    I would just set the emails_attributes from user_form_params in the user_form.rb as a setter method. That way you don't have to customize the form fields.

    Complete Answer:

    Models:

    #app/modeles/user.rb
    class User < ApplicationRecord
      has_many :user_emails
    end
    
    #app/modeles/user_email.rb
    class UserEmail < ApplicationRecord
      # contains the attribute: #email
      belongs_to :user
    end
    

    Form Objects:

    # app/forms/user_form.rb
    class UserForm
      include ActiveModel::Model
      include Virtus.model
    
      attribute :name, String
    
      validates :name, presence: true
      validate  :all_emails_valid
    
      attr_accessor :emails
    
      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|
          UserEmail.new(email: email.email_text)
        end
        user.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
    # "Embedded Value" Form Object.  Utilized within the user_form object.
    class EmailForm
      include ActiveModel::Model
      include Virtus.model
    
      attribute :email_text, String
    
      validates :email_text,  presence: true
    end
    

    Controller:

    # app/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
    

    Views:

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

    New User

    <%= render 'form', user_form: @user_form %> #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 %>

提交回复
热议问题