I’m using changesets to validate various API submissions and I’m wondering if someone has come up with an easy way to format the errors? It’s a lot of busy work to traverse the changeset errors and assemble a meaningful error message, so I thought I’d ask the forum people to see if someone had already solved that particular problem (yes, I’m fishing). Thank you!
2 Likes
I either assign them to form elements if there is an HTML form, or just loop over the errors and assign to a map to give out to json for an API. Can you give some examples of what you are trying to do?
traverse_errors/2 might help out
5 Likes
Here’s what I have… useful for formatting the changeset errors into a string for logging etc.
def changeset_error_to_string(changeset) do
Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end)
|> Enum.reduce("", fn {k, v}, acc ->
joined_errors = Enum.join(v, "; ")
"#{acc}#{k}: #{joined_errors}\n"
end)
end
It’ll return a string like
"password: Not enough special characters (only 0 instead of at least 1); Not enough numbers characters (only 0 instead of at least 1); Not enough upper_case characters (only 0 instead of at least 1)\n"
16 Likes
I had to add a case for when the traversed error value came as an error:
defp _to_string(val) when is_list(val) do
Enum.join(val, ",")
end
defp _to_string(val), do: to_string(val)
Full implementation:
defmodule Yourapp.Ecto.Errors do
def mapper(%Ecto.Changeset{} = changeset) do
IO.inspect(changeset)
Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", _to_string(value))
end)
end)
|> Enum.reduce("", fn {k, v}, acc ->
joined_errors = Enum.join(v, "; ")
"#{acc} #{k}: #{joined_errors}"
end)
end
def mapper(changeset), do: changeset
defp _to_string(val) when is_list(val) do
Enum.join(val, ",")
end
defp _to_string(val), do: to_string(val)
end
7 Likes