mrbuchmas

mrbuchmas

Error when attempting to use verified routes with ~p

Hey there, I’m new to the Elixir/Phoenix eco system, and I’ve been trying to model an application from some code from an online course, but I must have somethings setup wrong, because I get errors when trying to use certain features.

Specifically, I want to use verified routes via ~p and use the <.link> syntax, but I get the following errors:

error: undefined function sigil_p/2 (expected DroneShopWeb.DroneLive to define such a function or for it to be imported, but none are available)

lib/drone_shop_web/live/drone_live.ex:48: DroneShopWeb.DroneLive.render/1

error: undefined function link/1 (expected DroneShopWeb.DroneLive to define such a function or for it to be imported, but none are available)

lib/drone_shop_web/live/drone_live.ex:27: DroneShopWeb.DroneLive.render/1

Here is my code:

defmodule DroneShopWeb.DroneLive do
  use DroneShopWeb, :live_view


  alias DroneShop.Drones

  def mount(_params, _session, socket) do
    socket =
      assign(socket,
        filter: %{type: "", prices: []},
        drones: Drones.list_drones()
      )

    {:ok, socket}
  end

  def handle_params(%{"id" => id}, _uri, socket) do
    drone = Drones.get_drone!(id)
    {:noreply, assign(socket, selected_drone: drone, page_title: drone.name)}
  end

  def handle_params(_params, _uri, socket) do
    {:noreply, assign(socket, selected_drone: hd(socket.assigns.drones))}
  end


  def render(assigns) do
    ~H"""
      <h1 class="text-6xl text-center mb-10">Inventory</h1>
      <.filter_form filter={@filter} />
      <div class="flex flex-row flex-wrap justify-center">
        <%= for drone <- @drones do %>
          <div class="max-w-sm rounded overflow-hidden shadow-lg mx-4 mt-3">
            <img class="w-full" src={drone.image}>
            <.link phx-click="more_info"  # patch={~p"/drones?#{[id: drone.id]}"} class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mt-3">More Info</.link>
            <div class="px-6 py-4">
              <p class="font-bold text-xl mb-2"><%= drone.model %>
              </p>
              <p class="text-gray-700 text-base">
                <%= drone.description %>
              </p>
              </div>
          </div>
          <% end %>
      </div>
    """
  end

  def handle_event("more_info", %{"id" => id}, socket) do
    IO.inspect(self(), label: "More Info Click")
    drone = Drones.get_drone!(id)
    {:noreply, assign(socket, selected_drone: drone, page_title: drone.name)}
  end

  def filter_form(assigns) do
    ~H"""
    <form phx-change="filter" class="text-center mb-10">
      <div class="">
        <select name="type" class="rounded-lg">
          <%= Phoenix.HTML.Form.options_for_select(
            type_options(),
            @filter.type
          ) %>
        </select>
        <div class="prices">
          <%= for price <- ["$", "$$", "$$$"] do %>
            <input
            class="rounded"
              type="checkbox"
              name="prices[]"
              value={price}
              id={price}
              checked={price in @filter.prices}
            />
            <label for={price}><%= price %></label>
          <% end %>
          <input type="hidden" name="prices[]" value="" />
        </div>
      </div>
    </form>
    """
  end

  def handle_event("filter", %{"type" => type, "prices" => prices}, socket) do
    filter = %{type: type, prices: prices}
    drones = Drones.list_drones(filter)
    {:noreply, assign(socket, drones: drones, filter: filter)}
  end

  def handle_event("filter", %{"id" => id}, socket) do
    filter = %{id: id}
    drones = Drones.list_drones(filter)
    {:noreply, assign(socket, drones: drones, filter: filter)}
  end

  defp type_options do
    [
      "All Types": "",
      Cinematic: "Cinematic",
      FPV: "FPV"
    ]
  end
end

Showing Posts 1 to 10

sodapopcan

sodapopcan

What version of Phoenix and LiveView are you using? They weren’t available until Phoenix 1.17.

Eiji

Eiji

Please show this module.

In general sigils are just a functions with a bit of syntactic sugar (~ notation). Functions and macros are not “magically enabled”. They need to be defined or imported within a module. The only “magic” is that functions and macros defined in Kernel and Kernel.SpecialForms are automatically imported to every module. Since verified routes are not even part of Elixir core we need to define or import them.

# definition
defmodule MyLib do
  defmacro __using__(opts \\ []) do
    quote do
      def sigil_p(string, opts) do
        # …
      end
    end
  end
end

defmodule MyApp do
  # use is short for:
  # require MyLib
  # MyLib.__using__(…)
  use MyLib
end

# import
defmodule MyLib do
  def sigil_p(string, opts) do
    # …
  end 
end

defmodule MyApp do
  import MyLib
  # or use some macro with such import call
  # require MyLib
  # MyLib.macro_with_import_stuff(…)
  # or same macro, but with name __using__ and use call as above
end

When generating phoenix app with:

$ mix archive.install hex phx_new --force
$ mix phx.new drone_shop

it should create a file called drone_shop/lib/drone_shop_web.ex with such code:

defmodule DroneShopWeb do
  # …

  # this calls html_helpers private function …
  def live_view do
    quote do
      use Phoenix.LiveView,
        layout: {DroneShopWeb.Layouts, :app}

      unquote(html_helpers())
    end
  end

  # …

  # this function have some shared code for many parts of your application
  # at the bottom you should another call
  # this time to verified_routes private function
  defp html_helpers do
    quote do
      # …

      # Routes generation with the ~p sigil
      unquote(verified_routes())
    end
  end

  # we are finally here!
  # we are using some module
  # this is the second case I mentioned i.e. import within a macro
  def verified_routes do
    quote do
      use Phoenix.VerifiedRoutes,
        endpoint: DroneShopWeb.Endpoint,
        router: DroneShopWeb.Router,
        statics: DroneShopWeb.static_paths()
    end
  end

  @doc """
  When used, dispatch to the appropriate controller/view/etc.
  """
  # This is called first! It then call a live_view function …
  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end
end

You can find an import call in phoenix source:
https://github.com/phoenixframework/phoenix/blob/7b5cd358aadb507cd90aa3a52e013f9e9e947ac4/lib/phoenix/verified_routes.ex#L121-131

And the definition of said sigil is here:
https://github.com/phoenixframework/phoenix/blob/7b5cd358aadb507cd90aa3a52e013f9e9e947ac4/lib/phoenix/verified_routes.ex#L202-L210

SergeyMoiseev

SergeyMoiseev

use DroneShopWeb, :verified_routes

Will do it for you

Eiji

Eiji

In newly generated projects this code is wrong, because a live_view function should call it. This code would be good only if author would remove said call, but then there would be no question about it …

SergeyMoiseev

SergeyMoiseev

Verified routes are not enabled by default. Either this use or

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

Is needed for them to work.

Sources:

Eiji

Eiji

Not sure what do you mean … They are enabled by default. See my previous post explaining macros expansion. In newly generated phoenix app you can use routes without any extra change …

mrbuchmas

mrbuchmas OP

I am using the latest versions of both

mrbuchmas

mrbuchmas OP

Here is drone_shop/lib/drone_shop_web.ex

defmodule DroneShopWeb do
  @moduledoc """
  The entrypoint for defining your web interface, such
  as controllers, views, channels and so on.

  This can be used in your application as:

      use DroneShopWeb, :controller
      use DroneShopWeb, :view

  The definitions below will be executed for every view,
  controller, etc, so keep them short and clean, focused
  on imports, uses and aliases.

  Do NOT define functions inside the quoted expressions
  below. Instead, define any helper function in modules
  and import those modules here.
  """

  def controller do
    quote do
      use Phoenix.Controller, namespace: DroneShopWeb

      import Plug.Conn
      import DroneShopWeb.Gettext
      alias DroneShopWeb.Router.Helpers, as: Routes
    end
  end

  def view do
    quote do
      use Phoenix.View,
        root: "lib/drone_shop_web/templates",
        namespace: DroneShopWeb

      # Import convenience functions from controllers
      import Phoenix.Controller,
        only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]

      # Include shared imports and aliases for views
      unquote(view_helpers())
    end
  end

  def live_view do
    quote do
      use Phoenix.LiveView,
        layout: {DroneShopWeb.LayoutView, "live.html"}

      unquote(view_helpers())
    end
  end

  def live_component do
    quote do
      use Phoenix.LiveComponent

      unquote(view_helpers())
    end
  end

  def component do
    quote do
      use Phoenix.Component

      unquote(view_helpers())
    end
  end

  def router do
    quote do
      use Phoenix.Router

      import Plug.Conn
      import Phoenix.Controller
      import Phoenix.LiveView.Router
    end
  end

  def channel do
    quote do
      use Phoenix.Channel
      import DroneShopWeb.Gettext
    end
  end

  defp view_helpers do
    quote do
      # Use all HTML functionality (forms, tags, etc)
      use Phoenix.HTML

      # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
      import Phoenix.LiveView.Helpers

      # Import basic rendering functionality (render, render_layout, etc)
      import Phoenix.View

      import DroneShopWeb.ErrorHelpers
      import DroneShopWeb.Gettext
      alias DroneShopWeb.Router.Helpers, as: Routes
    end
  end

  @doc """
  When used, dispatch to the appropriate controller/view/etc.
  """
  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end
end

mrbuchmas

mrbuchmas OP

Do I need to specifically define the verified routes somewhere?

sodapopcan

sodapopcan

That doesn’t look like it was generated with the latest version. I’m assuming you did an update?

As per @SergeyMoiseev pointed out you need to add:

def verified_routes do
  quote do
    use Phoenix.VerifiedRoutes,
      endpoint: DroneShopWeb.Endpoint,
      router: DroneShopWeb.Router,
      statics: DroneShopWeb.static_paths()
  end
end

Then in the view_helpers function add:

unquote(verified_routes())

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews