Reputation: 2783
Here's a simple controller update action:
def update
note = Note.find(params[:id])
if note.update(note_params)
render json: note.to_json
else
render json: {errors: note.errors}, status: :unprocessable_entity
end
end
This renders errors in the form
{"errors":{"title":["can't be blank"]}}
but I want it in the form of
{"errors":{"title":["Title can't be blank"]}}
Simply using {errors: note.errors.full_messages}
gives
{:errors=>["Title can't be blank"]}
and misses the attribute keys.
The only way I can get it into the desired form seems to be a bit more involved:
full_messages_with_keys = note.errors.keys.inject({}) do |hash, key|
hash[key] = note.errors.full_messages_for(key)
hash
end
render json: {errors: full_messages_with_keys}, status: :unprocessable_entity
This works, but it seems odd that I have to do this since it seems to be a pretty common use case for doing validations on a SPA front-end. Is there a built-in method/more canonical way?
Upvotes: 2
Views: 2699
Reputation: 871
You can pass an additional argument to to_hash
or to_json
to generate messages that include the attribute name:
person.errors.to_hash(true)
or
person.errors.to_json(full_messsages: true)
will both return {"title":["Title can't be blank"]}
Upvotes: 1
Reputation: 102036
You can use ActiveModel::Errors#group_by_attribute
to get a hash of errors per key:
person.errors.group_by_attribute
# => {:name=>[<#ActiveModel::Error>, <#ActiveModel::Error>]}
From there is simply a matter of generating the full message from each ActiveModel::Error
instance:
note.errors
.group_by_attribute
.transform_values { |errors| errors.map(&:full_message) }
Is there a built-in method/more canonical way?
Not really. A framework can't cover every possible need. It provides the tools needed to format the errors however you want.
However instead of repeating this all across your controllers this functionality can be pushed into the model layer, a serializer or even monkeypatched onto ActiveModel::Errors
if you want to get crazy.
class ApplicationRecord < ActiveRecord::Base
def grouped_errors
errors.group_by_attribute
.transform_values { |errors| errors.map(&:full_message) }
end
end
Upvotes: 1