5

How can I change the error message for required fields? If I have something like that

@required_fields ~w(name email)

and I want to show "no way it's empty" instead of the default value of "can't be blank" ?

NoDisplayName
  • 15,246
  • 12
  • 62
  • 98

3 Answers3

10

I normally customize this way:

validate_required(changeset, [:email], message: "Email cannot be blank.")
NoDisplayName
  • 15,246
  • 12
  • 62
  • 98
  • It wasn't possible back in the day. Now that's the way to go. – NoDisplayName Sep 10 '17 at 12:41
  • 2
    this wouldn't work, the error message would come out as `email Email cannot be blank`. You are only able to change the ending and it always adds the field name on the front (and in lowercase). – Michael St Clair Jul 26 '18 at 22:01
8

The "can't be blank" error message is hardcoded into Ecto at the moment. However, you can replace this error message by doing:

def changeset(model, params \\ :empty) do
  model
  |> cast(params, @required_fields, @optional_fields)
  |> required_error_messages("no way it's empty")
end

def required_error_messages(changeset, new_error_message) do
  update_in changeset.errors, &Enum.map(&1, fn
    {key, "can't be blank"} -> {key, new_error_message}
    {_key, _error} = tuple  -> tuple
  end)
end

Hope that helps!

Gjaldon
  • 5,534
  • 24
  • 32
  • That's too bad that it's hardcoded :( Most of the people will probably like to change it – NoDisplayName Aug 16 '15 at 06:27
  • You're right. Went ahead and added an issue to Ecto https://github.com/elixir-lang/ecto/issues/884 so they add a way to support it. – Gjaldon Aug 16 '15 at 06:42
  • For now this is the way to go until the I18n system is working. – José Valim Aug 16 '15 at 07:53
  • There's a missing ")" after Enum.map. I couldn't edit it myself as it's less than 6 characters. – nsarno Aug 19 '15 at 21:09
  • Any concerns about using "def translate_error({msg, opts})" in generated ErrorHelpers to match on phrase and push out an own key/message ? – Alebon Oct 10 '16 at 08:38
  • Here with Ecto ~> 2.1, It's a bit different. ```update_in changeset.errors, &Enum.map(&1, fn {key, {"can't be blank", validations}} -> {key, {new_error_message, validations}} {_key, _error} = tuple -> tuple end)``` – Eduardo Pereira Aug 29 '17 at 12:37
1

I think Ecto.Changeset may have changed since the last answer was posted. As of ecto_sql 3.1, the %Ecto.Changeset{} struct stores errors like this:

errors: [address1: {"can't be blank", [validation: :required]}]

So I had to alter the structure of the previous solution a bit. In this example, I am using cast/4 to cast a raw map (the first argument may be a changeset or a data tuple as {data, types}):

@permitted [:name, :phone, :url]
@parameter_types %{name: :string, phone: :string, url: :string}

def signup_changeset(params) do
    IO.inspect params
    cast({%{}, @parameter_types}, params, @permitted)
    |> validate_required([:name, :phone, :url])
    |> required_error_messages("no way it's empty")
end

defp required_error_messages(changeset, new_error_message) do
    update_in changeset.errors, &Enum.map(&1, fn
      {key, {"can't be blank", rules}} -> {key, {new_error_message, rules}}
      tuple  -> tuple
    end)
end

Note that you have to call validate_required before you will have any default "can't be blank" strings.

Alternatively, you can verbosely set an error message for each field in violation:

@permitted [:name, :phone, :url]
@parameter_types %{name: :string, phone: :string, url: :string}

def signup_changeset(params) do
    cast({%{}, @parameter_types}, params, @permitted)
    |> validate_required(:name, message: "Dude. You need an address.")
    |> validate_required(:phone, message: "You must have a name.")
    |> validate_required(:url, message: "We need a valid URL for your homepage.")
  end
Everett
  • 8,746
  • 5
  • 35
  • 49