LiveComponent reusability, passing function in assigns

Hi, could you help me with this one?

I have a LiveComponent named ListIngredients that displays a list of ingredients in a form of a table. For each ingredient there are buttons allowing for different actions depending on the use case. This LiveComponent is reused in a few LiveViews in different contexts, for example:

  1. LV that allows for CRUD operations on listed ingredients
  2. LV that allows for adding one of listed ingredients to the meal

I wonder what would be the best approach to reuse ListIngredients component so that those buttons could act differently depending on the parent LV requirements. For now, I simply send a function from parent LiveView to ListIngredients in assigns but maybe there are better ways to accomplish this kind of reusability?

ListIngredients example usage in a parent LV render function:

<%=
	live_component(@socket, ListIngredients, [
		ingredients: @ingredients,
		fun1: &link_delete/2,
		fun2: &link_edit(&1, &2, IngredientLive.Edit)
	])
%>

ListIngredients Component (fragment of render function):

...
 
 <div class="list">
          <%= for ingredient <- @ingredients do %>
            <div class="ingredient">
              <div class="ingredient-details name">
                <%= ingredient.name %>
              </div>
              
              ...
              
              <div class="ingredient-buttons">
                <%= if Map.has_key?(assigns, :fun1) do @fun1.(@socket, ingredient.id) end %>
                <%= if Map.has_key?(assigns, :fun2) do @fun2.(@socket, ingredient.id) end %>
              </div>
            </div>
          <% end %>
          
 ...
1 Like

What exactly is different between the different renditions of ListIngredients? Could you just pass the set of actions that phx-click should be assigned to?

<%=
	live_component(@socket, ListIngredients, [
		ingredients: @ingredients,
		onDelete: "delete_foo",
                ...
	])

        ....
        <div class="ingredient-buttons">
           <%= if assigns[:onDelete], do: %> <button phx-click="<%= assigns[:onDelete] %>">Delete</button>

Hi, sorry for the late answer. Basically, main differences are links provided for buttons (normal link, live_redirect, live_patch) and parent type: LiveView, another LiveComponent (phx-target needs to be provided for when parent is LiveComponent but it can’t be provided if the parent is LV).

In a case where parent is LV I sent below functions through assigns:

  defp link_delete(socket, id) do
    link to: "#",
         phx_click: "delete",
         phx_value_id: id,
         data: [confirm: "Czy na pewno chcesz usunąć?"] do
      icon_tag(socket, "delete", class: "icon-svg")
    end
  end

  def link_edit(socket, id, module) do
    live_redirect to: Routes.live_path(socket, module, id) do
      icon_tag(socket, "edit", class: "icon-svg")
    end
  end

Another example where parent is LiveComponent:

 # Returns a link for deleting a particular ingredient
  defp link_delete(socket, id, meal_id) do
    link to: "#",
         phx_click: "delete",
         phx_target: "#vm-meal_#{meal_id}",
         phx_value_id: id,
         data: [confirm: "Czy na pewno chcesz usunąć?"] do
      icon_tag(socket, "delete", class: "icon-svg")
    end
  end

  # Returns a link for editing an ingredient's weight
  defp link_edit_ingredient_weight(socket, id, meal) do
    live_patch to:
                 Routes.live_path(
                   socket,
                   Edit,
                   meal.id,
                   step: "view-weight",
                   ingredient_id: id,
                   action: "change"
                 ) do
      icon_tag(socket, "edit", class: "icon-svg")
    end
  end

So, I’m not sure it would be doable sending only action through assigns.

Possibly, I could do this in a way you presented, that is using only phx-click on a button and do any further redirects, patches inside handle_event function in the parent, but this time using equivalents: push_patch, push_redirect. Then I would only need to handle phx-target case when parent is either LC or LV. I’ll check this out.