Delete a record from db using liveview

I am following a tutorial, and trying to delete a record from database using liveview. All the examples that I saw on different resources on the internet about Phoenix LiveView use the following:

<span><%= link "Delete", to: "#", phx_click: "delete", phx_value_id: product.id, data: [confirm: "Are you sure?"] %></span>
@impl true
  def handle_event("delete", %{"id" => id}, socket) do
    product = Store.get_product!(id)
    {:ok, _} = Store.delete_product(faq)

    {:noreply, assign(socket, :products, list_products())}
  end

  defp list_products do
    Store.list_products()
  end

this will delete the record, but I will have to refresh every-time to update my index page.

why not use <%= live_patch %> like in an update event?

You are assigning the products with list_products, which should update your view. What’s the template code look like?

in my index.htm.heex

<%= if assigns[:current_user] do %>
    <div id="products" phx-update="prepend">
      <%= for products <- @products do %>
        <%= live_component(StoreWeb.PorductLive.ProductComponent,
          id: product.id,
          product: product,
          current_user_id: @current_user.id
        ) %>
      <% end %>
    </div>
  <% end %>

and in my product_component.ex

def render(assigns) do
    ~H"""
      <div id={"product-#{@product.id}"} class="product">
        <h4>
          <strong>
          <%= live_patch @product.name, to: Routes.product_show_path(@socket, :show, @product.id) %>
          </strong>
          <%= @product.name %>
        </h4>
        
          <%= if assigns[:current_user_id] do %>
            <%= if(@current_user_id == @product.user.id) do %>
              <u>
                <%= live_patch "Edit Product", to: Routes.product_index_path(@socket, :edit, @product.id) %>
              </u>
              <u>
                <%= link "Delete", to: "#", phx_click: "delete", phx_value_id: @product.id, data: [confirm: "Are you sure?"] %>
              </u>
            <% end %>
          <% end %>
        </div>
        <u>
        <%= live_patch "Details", to: Routes.product_show_path(@socket, :show, @product.id) %>
        </u>
      </div>
    """
  end

I think it might have to do with the phx-update. I think we need to have the unique DOM element right after to track the changes.

Try to put the DOM id right after the for loop like this (also update the component ID to not clash with this):

<%= for product <- @products do %>
  <div id={"product-#{product}"} class="product">
    <%= live_component(StoreWeb.PorductLive.ProductComponent,
        id: product.id,
        product: product,
        current_user_id: @current_user.id
      ) %>
  </div>
<% end %>

See DOM patching

1 Like

Isn’t that overwriting the id Phoenix would give to it?

And why is this a Live component and not a function component?

1 Like

your answer help me to look up the difference. And I removed the product_component.ex and added inline html inside the index.html.heex instead, and it is working in fact. I was also mixing between LEEX and HEEX annotations, so that probably contributed to the issue.

added this to my index.html.heex and removed the product_component.ex file

<tbody id="products">
        <%= for product <- @products do %>
          <tr id={"product-#{product.id}"}>
            <td><%= product.name %></td>

            <%= if(@current_user.id == product.user.id) do %>
              <td>
                <span>
                  <%= live_patch("Edit",
                    to: Routes.product_index_path(@socket, :edit, product)
                  ) %>
                </span>
              </td>
              <td>
                <span>
                  <%= link("Delete",
                    to: "#",
                    phx_click: "delete",
                    phx_value_id: product.id,
                    data: [confirm: "Are you sure?"]
                  ) %>
                </span>
              </td>
            <% end %>
            <td>
              <%= live_redirect("Details",
                to: Routes.product_show_path(@socket, :show, product)
              ) %>
            </td>
          </tr>
        <% end %>
      </tbody>
    </table>

If you want to use a Function Component instead, change the above to use Endpoint. Routes.product_index_path(Endpoint, :edit, product)