rectify icon indicating copy to clipboard operation
rectify copied to clipboard

Validation errors on nested forms

Open bramj opened this issue 9 years ago • 3 comments

Hi @andypike,

First off: thanks a lot for your work on Rectify!

So I've been rectifying a rather big, complex form and been running into some questions. One of those is: how do you display errors on nested forms?

Take for instance the blog example again:

class Blog < ActiveRecord::Base
  has_many :posts
end

class Post < ActiveRecord::Base
  belongs_to :blog
end

class BlogForm < Rectify::Form
  attribute :title, String
  attribute :posts, Array[PostForm]
end

class PostForm < Rectify::Form
  attribute :title, String
  attribute :body, String
end

Now in the view, we want to show validation errors. Typically, this is something like this:

-# app/views/blogs/_form.html.haml
- if form.errors.any?
  .alert
    %h4
      = pluralize(form.errors.count, 'error')
      prohibited this blog from being saved:
    %ul
      - form.errors.each do |key, message|
        %li= "#{key} #{message}"

Now if we have a validation error on a PostForm, this error will not be displayed, since it's in e.g. form.post.first.errors.

It's not difficult to loop over all the errors of nested models, but if you have many nested forms this becomes tedious. Do you think it would be nice to be able to do something like form.all_errors?

bramj avatar Aug 04 '16 15:08 bramj

Thanks for the question! Sorry for the delayed response but I've been away on vacation recently. Let me digest this a little and I'll get back to you. Thanks for your patience.

andypike avatar Aug 25 '16 07:08 andypike

+1

scarroll32 avatar Feb 05 '17 10:02 scarroll32

Here's a bit of a workaround for anyone else that might need it (thanks to @BobFromAccounting):

class ApplicationForm < Rectify::Form
  def merge_errors_for(attr, field_prefix = '')
    field_prefix = field_prefix.presence || attr.to_s

    public_send(attr.to_s).errors.messages.each do |field, errors_array|
      errors_array.each do |error_message|
        errors.add("#{field_prefix}_#{field}", error_message)
      end
    end
  end
end

class AccountForm < ApplicationForm
  attribute :name, String
  attribute :monthly_price, Integer
  attribute :office, OfficeForm
  attribute :user, UserForm

  validates :name, :monthly_price, :max_employees,
              presence: true
  validate :office_form_is_valid
  validate :user_form_is_valid

  private

  def office_form_is_valid
    if office.invalid?
      merge_errors_for(:office)
    end
  end

  def user_form_is_valid
    if user.invalid?
      merge_errors_for(:user, 'owner')
    end
  end
end

laurenfackler avatar Jun 26 '17 20:06 laurenfackler