reform
reform copied to clipboard
Errors of nested collection are not shown.
Complete Description of Issue
Hi! I'm using reform 2.5.0 with dry-validations 1.6 into rails 6.0.3 and seeing issue. I'm trying to validate a nested collection of forms from the parent form and output errors for each collection item, but form.errors.messages
method returns empty hash.
Steps to reproduce
class WorkForm < Reform::Form
property :id
property :claim_id
property :group_id
collection :workflows, form: WorkflowForm, populate_if_empty: Workflow
validation do
option :form
config.messages.backend = :i18n
params do
required(:group_id).filled(:int?)
optional(:workflows)
end
rule(:group_id) do
key.failure(:already_exist) if Work.where.not(id: form.model.id).where(group_id: value, claim_id: form.claim_id).exists?
end
rule(:workflows).each do |index:|
next if value[:id]
# For reproduce I removed any conditions and just raised error to fail validation
key([:workflows, :sender_id, index]).failure('invalid sender_id')
end
end
end
class WorkflowForm < Reform::Form
property :id
property :work_id
property :sender_id
property :message
end
# Now call the form
irb(main):366:0> params = { "group_id": 2, "workflows": [{ "sender_id": 4, "message": "workflow message" }] }
irb(main):366:0> form = WorkForm.new(Work.find(4))
irb(main):366:0> form.validate(params)
# false
irb(main):366:0> form.errors.messages
# {}
Expected behavior
I did this validation according to dry-rb documentation: https://dry-rb.org/gems/dry-validation/1.6/rules/#defining-a-rule-for-each-element-of-an-array
I expect to see something like this:
irb(main):366:0> form.errors.messages
# {"workflows"=>{"sender_id"=>{0=>["invalid sender_id"]}}}
Actual behavior
But now I see an empty hash
irb(main):366:0> form.errors.messages
# {}
Although if you look at the error object, you can see that the validation worked successfully and detected an error into collection:
irb(main):366:0> form.errors.instance_variable_get(:@result)
=> #<Reform::Contract::Result:0x0000563bef736a28 @results=[#<Dry::Validation::Result{:group_id=>2, :workflows=>[{:id=>nil, :claim_id=>nil, :work_id=>nil, :sender_id=>4, :message=>"workflow message"}]} errors={:workflows=>{:sender_id=>{0=>["invalid sender_id"]}}}>], @failure=#<Dry::Validation::Result{:group_id=>2, :workflows=>[{:id=>nil, :claim_id=>nil, :work_id=>nil, :sender_id=>4, :message=>"workflow message"}]} errors={:workflows=>{:sender_id=>{0=>["invalid sender_id"]}}}>>
If I transfer the validation to the WorkflowForm and refuse to iterate the array, there is no such problem, the error is successfully displayed. But I cannot transfer validation to a nested form as I need to compare the data from WorkForm
with data from WorkflowForm
form.
System configuration
Reform version: 2.5.0 Rails version: 6.0.3 dry-validation version: 1.6.0
This works for me
require 'test_helper'
class ValidateCollectionTest < MiniTest::Spec
class SongForm < TestForm
property :title
end
class AlbumForm < TestForm
collection :songs, form: SongForm
validation do
option :form
params do
optional(:songs)
end
rule(:songs).each do |index:|
key([:title, index]).failure('Invalid') if value[:title] == "Wrong"
end
end
end
let(:song) { Song.new("Broken") }
let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
let(:composer) { Artist.new("Greg Graffin") }
let(:artist) { Artist.new("Bad Religion") }
let(:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
let(:form) { AlbumForm.new(album) }
it do
form.validate("songs" => [{"title" => "Wrong"}, {"title" => "Brok"}])
_(form.errors.messages).must_equal({:title=>{0=>["Invalid"]}})
end
end
Try replacing next if value[:id]
with next if form.model.id
as id
isn't present in your params
.