How to show flash and stay on the same page - custom checking

Hey there,
How could I show a flash error and stay on the same page if there is something wrong with the data that is being sent?
I am checking manually if it is wrong but the page goes back to the deafult path, doesnt stay on /new and the data is gone too from the page:

if length(check_by_challenge_group) > 0 do
    changeset = Ecto.Changeset

    conn
    |> put_flash(:error, "There is a main Challenge for this Challenge Group already!")
    |> render("new.html", changeset: changeset, challenge_groups: challenge_groups)
  else

Also getting the changeset like this is not good, so what is the correct way?

ok after posting it i got it :grin:
like this:

if length(check_by_challenge_group) > 0 do
    changeset = Challenge.changeset(%Challenge{}, challenge_params)

    conn
    |> put_flash(:error, "There is a main Challenge for this Challenge Group already!")
    |> render("new.html", changeset: changeset, challenge_groups: challenge_groups)

Actually I am still not getting redirected to the correct path
the id disappears from creating a new one and for editing the /edit disappears*
ok it is working, but the path is not what it should be
to explain it a bit more this is how it goes:
I start here:

http://0.0.0.0:4000/challenges

i go here first:

http://0.0.0.0:4000/challenges/new
then make an error, to get the flash from the code above
and then i am at this path again but the view is the previous one, the new.html

http://0.0.0.0:4000/challenges

and if i fix the issue, and submit i go to the show page:

http://0.0.0.0:4000/challenges/12

it happens similarly with the edit path, just there the id disappears

Show me your router.ex file and controller where edit function is at.

ok,
the router:

scope "/", Userteam1Web do
# Use the default browser stack
pipe_through(:browser)

resources("/", SessionController, only: [:index, :create, :delete])
get("/index", PageController, :index)
resources("/teams", TeamController)
resources("/users", UserController)
resources("/roles", RoleController)
resources("/challenges", ChallengeController)
resources("/info", InfoController)
resources("/challengegroups", ChallengeGroupController)
end

and the controller edit/update:

def edit(conn, %{"id" => id}) do
challenge = Web.get_challenge!(id)
changeset = Web.change_challenge(challenge)

challenge_groups =
  Repo.all(Userteam1.Web.ChallengeGroup)
  |> Enum.map(&{&1.name, &1.id})

challenge_groups = [{"", ""} | challenge_groups]

render(conn, "edit.html",
  challenge: challenge,
  changeset: changeset,
  challenge_groups: challenge_groups
)
  end

  def update(conn, %{"id" => id, "challenge" => challenge_params}) do
    challenge = Web.get_challenge!(id)
    changeset = Web.change_challenge(challenge)

challenge_groups =
  Repo.all(Userteam1.Web.ChallengeGroup)
  |> Enum.map(&{&1.name, &1.id})

challenge_groups = [{"", ""} | challenge_groups]

check_by_challenge_group =
  if challenge_params["challenge_group_id"] != "" do
    get_challenge_by_challenge_group(challenge_params["challenge_group_id"])
  end

if challenge_params["main"] == "true" do
  if length(check_by_challenge_group) > 0 do
    conn
    |> put_flash(:error, "There is a main Challenge for this Challenge Group already!")
    |> render("edit.html",
      challenge: challenge,
      changeset: changeset,
      challenge_groups: challenge_groups
    )
  else
    case Web.update_challenge(challenge, challenge_params) do
      {:ok, challenge} ->
        conn
        |> put_flash(:info, "Challenge updated successfully.")
        |> redirect(to: challenge_path(conn, :show, challenge))

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "edit.html",
          challenge: challenge,
          changeset: changeset,
          challenge_groups: challenge_groups
        )
    end
  end
else
  if challenge_params["challenge_group_id"] == "" do
    challenge_params = Map.put(challenge_params, "main", true)

    case Web.update_challenge(challenge, challenge_params) do
      {:ok, challenge} ->
        conn
        |> put_flash(:info, "Challenge updated successfully.")
        |> redirect(to: challenge_path(conn, :show, challenge))

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "edit.html",
          challenge: challenge,
          changeset: changeset,
          challenge_groups: challenge_groups
        )
    end
  else
    case Web.update_challenge(challenge, challenge_params) do
      {:ok, challenge} ->
        conn
        |> put_flash(:info, "Challenge updated successfully.")
        |> redirect(to: challenge_path(conn, :show, challenge))

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "edit.html",
          challenge: challenge,
          changeset: changeset,
          challenge_groups: challenge_groups
        )
    end
  end
end
end

That’s actually expected behavior:

When you submit the form on the new page, it get’s posted to /challenges. You can check this by inspecting the html and looking at the action attribute of the form. When you do not redirect from the controller but instead render some html, the path won’t change and you’ll be on /challenges.

It usually is not a problem. Is it for your use case?

so far it seems to be working fine, just it was surprising to me, since I did not expect that behavior, I thought I missed something

I believe you can add manually a function to your controller with the name, for example, edit_challenge and afterwards make a route for it. It should look like this I think: get("/edit_challenge/:id", PageController, :edit_challenge). This will represent your edit function as you put the same code here. In this way it should show the id in the url.
Example: localhost:4000/challenge/2

Or exclude the edit from resources resources("/challenges", ChallengeController) only: [:show, :index etc... (do not include :edit here) ] and make your own edit route like the example I gave above.

1 Like