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.

10 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
6 Likes