Preventing a stream_delete

I have a simple user admin LiveView page which uses streams to list users in a table and provides a delete link on each user row. Users have roles defined as a list of atoms e.g. [:user] or [:admin, :user]

I’m trying to prevent the handle_event("delete",...) call from deleting a user if they have the :admin role. I can prevent the deletion in the Repo easily enough but the table row in the LiveView is still being removed.

The handle_event code is:

  @impl true
  def handle_event("delete", %{"id" => id}, socket) do
    user = Accounts.get_user!(id)

    unless Enum.member?(user.roles, :admin) do
      {:ok, _} = Accounts.delete_user(user)

      {:noreply, stream_delete(socket, :users, user)}
    else
      {:noreply, socket}
    end
  end

Anyone who can tell me what’s the obvious factor that am I missing here?

@almarrs Did you consider to not render the delete user button/link?

~H"""
<.button <!-- other attrs --> :if={not Enum.member?(user.roles, :admin)}>
  <!-- contents -->
</.button>
"""
1 Like

A less jarring approach to some buttons being visible vs not would be to set them disabled instead. The button is then visible but clicking does nothing. You could also change the cursor but I think it does that for you.

Can you show the code you have for the button in the template?

If that LiveView user index page was created via the mix phx.gen.live, the phx-click binding will use LiveView’s client side utility command JS.hide/2 to hide the row after pushing the delete event back to the server.

# lib/my_app_web/live/user_live/index.html.heex
    <.link
      phx-click={JS.push("delete", value: %{id: user.id}) |> hide("##{id}")}
      data-confirm="Are you sure?"
    >
      Delete
    </.link>

# lib/my_app_web/components/core_components.ex
  def hide(js \\ %JS{}, selector) do
    JS.hide(js,
      to: selector,
      time: 200,
      transition:
        {"transition-all transform ease-in duration-200",
         "opacity-100 translate-y-0 sm:scale-100",
         "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"}
    )
  end

Once you remove |> hide("##{id}") from the phx-click binding, it should no longer be removed on the client side when not deleting a user because it has an :admin role on the server side. That said, I second the suggestions by @Eiji and @w0rd-driven to conditionally remove or disable the delete button from a UI/UX standpoint.

p.s. Welcome to ElixirForum!

1 Like

Thank you, @codeanpeace for explaining why it’s being removed and @Eiji for the suggestion - that’s the approach I’ve gone for as the UI/UX is better as highlighted by others on this thread.