Can I use p-sigil in H-sigil?

Can I use the new p-sigil in a H-sigil?

defmodule MyProjectWeb.Navbar do
  use Phoenix.Component

  def navbar(assigns) do
    ~H"""
    <div class="navbar bg-primary">
      <a href={~p"/"}>Home</a>
    </div>
    """
  end
end

When I do this I get a compile error

“(CompileError) undefined function sigil_p/2 (expected MyProjectWeb.Navbar to define such a function or for it to be imported, but none are available)”

Am I doing something wrong, or is this not possible?

1 Like

You’re not importing the verified routes module. Phoenix.Component does not include sigil_p.

Switch from

use Phoenix.Component

to

use MyProjectWeb, :html

This should get you working. The MyProjectWeb.html macro imports everything you need.

8 Likes

Oke, Thanks! This worked, going through the macro also helped me better understand

Using Phoenix 1.7.0-rc-2, I get the following warning when attempting to use the workaround mentioned above. Switching out use Phoenix.Component with use TodoListWeb, :html results in this error:

(UndefinedFunctionError) function TodoListWeb.BaseComponents.module_info/1 is undefined (function not available)
    (todo_list 0.1.0) TodoListWeb.BaseComponents.module_info(:exports)

Stacktrace:
  │ (elixir 1.14.2) src/elixir_import.erl:98: :elixir_import.calculate/6
  │ (elixir 1.14.2) src/elixir_import.erl:28: :elixir_import.import/4 (Elixir)

Any ideas?

Funny enough I just ran into the same issue for the first time just now, about 2 hours later. :slight_smile:

I think your and my issue are the same - that we’re trying to create our components in a module that is also included in the html_helpers macro that html unquotes. So you end up with a circular reference.

In RobinBeekhof’s original question, he’s doing this in a singular module MyProjectWeb.Navbar, so it wouldn’t be included as a core component and create the import loop.

I ended up fixing it by creating an extra helper in my myapp_web.ex, called html_helpers_plus which looks like this:

defp html_helpers_plus do
    quote do
      use Phoenix.Component

      # Include general helpers for rendering HTML
      unquote(html_helpers())

      # Custom application components using verified routes
      import MyAppWeb.AppComponents
    end
  end

I.e. it just unquotes html_helpers and then imports my app components that want to use verified routes. That way in AppComponents I can have the use MyAppWeb, :html line and not get the circular dependency.

I then import that in the live_view macro defined in the same file:

  def live_view do
    quote do
      use Phoenix.LiveView,
        layout: {MyAppWeb.Layouts, :app}

      unquote(html_helpers_plus())
    end
  end

and then any of my live views can automatically access the verified routes using components.

In dead views you can still import the components directly in the xyz_html.ex before the embed_templates call.

I’d love to know if there is a more elegant way to solve the issue though :slight_smile:

3 Likes

A more up-to-date solution is mentioned here:

It showed up as the error: warning: undefined function sigil_p/2.
The fix was to add the following line to the top of my component file.

use Phoenix.VerifiedRoutes,
  endpoint: MyAppWeb.Endpoint,
  router: MyAppWeb.Router
5 Likes