elixirphoenix-framework

Elixir Phoenix: one does not simply generate a link


I can't seem to get the link function to be found, when using it in heex.

index.html.heex

            <%= link to: "/numbers/#{number.id}/messages", class: "block w-full h-full" do %>
              Go
            <% end %>

controllers/number_html.ex

defmodule MyApp.NumberHTML do
  use MyAppWeb, :html

  import Phoenix.HTML # This line doesn't fix it

  embed_templates "number_html/*"

end

Here's the error I get:

Compiling 1 file (.ex)
    error: undefined function link/2 (expected MyApp.NumberHTML to define such a function or for it to be imported, but none are available)
    │
 18 │             <%= link to: "/numbers/#{number.id}/messages", class: "block w-full h-full" do %>
    │                 ^^^^
    │
    └─ (my_app 0.1.0) lib/my_app/controllers/number_html/index.html.heex:18:17: MyApp.NumberHTML.index/1

I must be missing something simple.


Solution

  • In phoenix_html 4.0, a number of HTML-generation functions were removed from the core. These moved into a new phoenix_html_helpers library, including PhoenixHTMLHelpers.Link.link/2. An easy approach is to take the workaround described in the phoenix_html changelog and

    1. Add a dependency in mix.exs on
      {:phoenix_html_helpers, "~> 1.0"}
      
    2. Import those in your my_app.ex view helpers
      import Phoenix.HTML
      import Phoenix.HTML.Form
      use PhoenixHTMLHelpers
      

    phoenix_live_view 0.18 added a number of corresponding Phoenix components that replace these, so in the more modern stack you'd use <.link to={...} /> rather than <%= link to: ... %>. You again probably need to add the newer helpers to your my_app.ex view helpers

    use Phoenix.Component
    

    and then your HEEx template changes to use the newer component syntax

    <.link to={"/numbers/#{number.id}/messages"} class: "block w-full h-full">
      Go
    </.link>
    

    If you also adopt the newer verified routes setup and your model object implements the Phoenix.Param protocol (which includes any struct with an id field), it would look a little more like

    <.link to={~p"/numbers/#{number}/messages"} class: "block w-full h-full">
      Go
    </.link>