2

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?

1
  • 1
    The only two built-in options on ActiveModel::Errors to get the humanized/translated attribute name in the message are full_messages and full_messages_for. Rolling your own with inject like you have would be how I would do it. Commented Aug 6, 2021 at 5:45

2 Answers 2

2

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"]}

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.