Hi,
I come back to Elixir every now and then and I am a bit rusty when it comes to pattern matching on functions. I am trying to come up with an elegant solution that will traverse all changeset errors with nested associations. I had it working, but then came across a situation that tripped up my original code.
# I want this
%{
order_products: [
%{product_code: ["can't be blank"]},
%{product_code: ["can't be blank"]}
],
post_code: ["can't be blank"]
}
#To become this:
%{
product_code: "can't be blank",
post_code: "can't be blank"
}
#This was my original code which builds upon the documenation for .traverse_errors(), but it breaks when there is an empty map for some entries e.g:
# %{
# order_products: [%{product_code: ["can't be blank"]}, %{}],
# post_code: ["can't be blank"]
# }
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 ->
extracted_errors = extract_errors(k, v)
"#{acc}#{extracted_errors}"
end)
end
# one level of nested extraction, not sure it would work for two levels
def extract_errors(_, [errors = %{}]) do
Enum.reduce(errors, "", fn {k, v}, acc ->
extracted_errors = extract_errors(k, v)
"#{acc}#{extracted_errors}"
end)
end
def extract_errors(property, errors) when is_list(errors) do
joined_errors = Enum.join(errors, "; ")
"#{property}: #{joined_errors}<br>"
end
Does anyone have a nice helper function that will deal neatly with a nested changeset