Validation errors on nested forms
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?
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.
+1
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