thojanssens1
How to print changeset errors without using the changeset in form_for?
I have a login page which allows to authenticate an admin.
If there are no users at all in the database (first time the app runs), I will create the superuser based on the login data entered (email/password).
My login form looks like this:
<h1>Login</h1>
<%= form_for @conn,
Routes.admin_session_path(@conn, :create),
[as: :session],
fn f -> %>
<div>
<%= text_input f, :email, placeholder: "Email" %>
<%= error_tag f, :email %>
</div>
<div>
<%= password_input f, :password, placeholder: "Password" %>
<%= error_tag f, :password %>
</div>
<%= submit "Log in" %>
<% end %>
And my controller actions:
def new(conn, _params) do
render(conn, "new.html")
end
def create(conn, %{"session" => %{"email" => email, "password" => pass}}) do
case Accounts.authenticate_by_email_and_pass(email, pass) do
{:ok, %{"active" => true} = user} ->
conn
|> MyAppWeb.Authentication.login(user)
|> put_flash(:info, "Welcome back!")
|> redirect(to: Routes.admin_question_path(conn, :index))
{:ok, %{"active" => false}} ->
conn
|> put_flash(:error, "Account is disabled")
|> render("new.html")
{:error, _reason} ->
case Accounts.count_users() do
0 ->
case Accounts.register_user(%{"name" => "Superuser", "email" => email, "password" => pass, "type" => "admin"}) do
{:ok, user} ->
conn
|> put_flash(:info, "#{user.name} created!")
|> redirect(to: Routes.admin_session_path(conn, :new))
{:error, %Ecto.Changeset{} = changeset} ->
conn
|> put_flash(:error, changeset.errors)
|> render("new.html")
end
_ ->
conn
|> put_flash(:error, "Invalid username/password combination")
|> render("new.html")
end
end
end
Of course as expected I have a problem on this line:
put_flash(:error, changeset.errors)
But as the login form is not displayed based on a changeset (but rather uses the connection: form_for @conn), how do I display the list of changeset errors when the superuser is being attempted to be created?
Marked As Solved
idi527
![]()
Check out Ecto.Changeset.traverse_errors/2:
errors = 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)
error_msg =
errors
|> Enum.map(fn {key, errors} -> "#{key}: #{Enum.join(errors, ", ")}")
|> Enum.join("\n")
put_flash(conn, :error, error_msg)
Also note that ecto schema structs have atom keys, so these two clauses might not match:
case Accounts.authenticate_by_email_and_pass(email, pass) do
{:ok, %{"active" => true} = user} -> # <----- this one
conn
|> MyAppWeb.Authentication.login(user)
|> put_flash(:info, "Welcome back!")
|> redirect(to: Routes.admin_question_path(conn, :index))
{:ok, %{"active" => false}} -> # <---- and this one
conn
|> put_flash(:error, "Account is disabled")
|> render("new.html")
Also Liked
lucaong
NobbZ
Or traverse, print and format the errors in the view, rather than the controller…
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








