How to interpolate sigil_H like sigil_E?

Continuing this thread How to replace “direct/simple” sigil_E to new sigil_H to avoid warnings?, to replace all sigil_E for the new sigil_H.
What is the best approach to interpolate sigil_H (inside other sigil_H parts)?
Is there a simple way to build html in parts with sigil_H?
It’s realy strange the whole assigns requirement for anything other than “new liveview render” or sigil_E be deprecated at all.

sigil_E (example)

  import Phoenix.HTML

  def breadcrumb(list) do
    list_count =
      list
      |> Enum.count
      |> Kernel.-(1)
    li =
      list
      |> Enum.with_index()
      |> Enum.map(fn {a, b} ->
        text =
          Translate.translate(a, false)
        case b == list_count do
          true ->
            ~E"""
              <li class="breadcrumb-item active"><%= text %></li>
            """
          false ->
            ~E"""
              <li class="breadcrumb-item"><%= text %></li>
            """
        end
      end)
    _html =
      ~E"""
      <ol class="breadcrumb">
        <%= li %>
      </ol>
      """
  end

sigil_H (example)

  use Phoenix.Component
  import Phoenix.HTML, only: [raw: 1]

  def breadcrumb(list) do
    assigns =
      %{}
    list_count =
      list
      |> Enum.count
      |> Kernel.-(1)
    li =
      list
      |> Enum.with_index()
      |> Enum.map(fn {a, b} ->
        text =
          Translate.translate(a, false)
        case b == list_count do
          true ->
            ~H"""
              <li class="breadcrumb-item active"><%= text %></li>
            """
          false ->
            ~H"""
              <li class="breadcrumb-item"><%= text %></li>
            """
        end
        |> Phoenix.HTML.Safe.to_iodata()
        |> List.to_string()
      end)
      |> List.to_string()
    _html =
      ~H"""
      <ol class="breadcrumb">
        <%= raw(li) %>
      </ol>
      """
  end

Expected result (in both cases)

<ol class="breadcrumb">
  <li class="breadcrumb-item">Resultados</li>
  <li class="breadcrumb-item">Painéis</li>
  <li class="breadcrumb-item active">Principal</li>
</ol>

If you change you breadcrumb/1 to:

use Phoenix.Component
  import Phoenix.HTML, only: [raw: 1]

  def breadcrumb(assigns) do
    list = assigns[:list] # Fetch list from assigns
    list_count =
      list
      |> Enum.count
      |> Kernel.-(1)
    li =
      list
      |> Enum.with_index()
      |> Enum.map(fn {a, b} ->
        text =
          Translate.translate(a, false)
        case b == list_count do
          true ->
            ~H"""
              <li class="breadcrumb-item active"><%= text %></li>
            """
          false ->
            ~H"""
              <li class="breadcrumb-item"><%= text %></li>
            """
        end
        |> Phoenix.HTML.Safe.to_iodata()
        |> List.to_string()
      end)
      |> List.to_string()
    _html =
      ~H"""
      <ol class="breadcrumb">
        <%= raw(li) %>
      </ol>
      """
  end

you should be able to call it like (you need to import the module first, or reference it with it’s complete name):

<.breadcrumb list={@your_list} />

and it should produce the same list as the sigil_E one.

You’d probably best of converting this to a function component. Not everything possible with the ~E engine can or should be done with heex. This is one of those, where I’d consider this misuse of heex.

# <.breadcrumb list={@list}>
def breadcrumb(assigns) do
  {last, path} = List.pop_at(assigns.list, -1)

  assigns = 
    assigns 
    |> assign(:path, path) 
    |> assign(:last, last)

  ~H""""
  <ol class="breadcrumb">
    <%= for item <- @path do %>
      <li class="breadcrumb-item"><%= item %></li>
    <% end %>
    <%= if @last do %>
      <li class="breadcrumb-item active"><%= @last %></li>
    <% end %>
  </ol>
  """
end

Heex is not just useful for liveview, but also does html validation as well as offer way better composability. I do think sigil_E was deprecated to quickly, but there’s no need to keep it around in the long run.

2 Likes