(edit 2: the error I thought Iād resolve was not resolved after all, so reverted to initial post, then added some more error output.)
Well, I got myself blocked again. (n00bs, man. sheesh.)
After several hours of googling, reading docs, tutorials, blog posts, slackexchange threads, searching these forums and reading threads, and trying different things, the error remains. So I thought Iād better āpull off of the highway to ask someone for directionsā again (thatās a reference to a time before GPS. lol)
I get the feeling Iām missing something obvious that an actual developer would have already thought of, so please feel free to mention things that should be obvious to people who actually know what theyāre doing. 
How I got here: Iām able to get the search bar to appear where I want it, but clicking on it has no effect, other than moving the border from left and top, to bottom and right when Iām holding down the left mouse button, then it goes back to left and to when I release the left-mouse button. But there are no additional errors generated in the logs in the phx server terminal window, and no errors in Chromeās dev Console, when I click in the search bar. (this behavior remains even after the renamings described below.)
So, I did some renaming after realizing the naming shown in my previous post may conflict with a def monologues(conn, params) do
I already had in lib/mono_phoenix_v01_web/controllers/monologues_page_controller.ex
(it includes an ecto query, with a join, and one of the tables is named monologues
)
After the renamings, Iām getting:
warning: no route path for MonoPhoenixV01Web.Router matches "/monofinds/#{monofind.slug}"
lib/mono_phoenix_v01_web/live/searchbar_live.html.heex:87: MonoPhoenixV01Web.SearchbarLive.render/1
(As you can see there, I have included support for verified routes when upgrading from Phoenix 1.6.15 to 1.7.1.)
Hereās that snippet from searchbar_live.html.heex
:
<ul
:if={@monofinds != []}
class="divide-y divide-slate-200 overflow-y-auto rounded-b-lg border-t border-slate-200 text-md leading-6"
id="searchbox__results_list"
role="listbox"
>
<%= for monofind <- @monofinds do %>
<li id={"#{monofind.id}"}>
<.link
navigate={~p"/monofinds/#{monofind.slug}"}
class="block p-4 hover:bg-slate-100 focus:outline-none focus:bg-slate-100 focus:text-sky-800"
>
<%= monofind.body %>
</.link>
</li>
<% end %>
</ul>
Then I noticed in my root.html.heex Iād commented out /assets/app.js
when I was troubleshooting something, and forgot to restore it, so I uncommented it.
I also uncommented liveSocket.enableDebug()
. I got some new info. This in the phx server console logs:
[info] Sent 200 in 4ms
[info] CONNECTED TO Phoenix.LiveView.Socket in 75µs
Transport: :websocket
Serializer: Phoenix.Socket.V2.JSONSerializer
Parameters: %{"_csrf_token" => "JjUcMwBFPQ1MOUcSZQ1GeDkEaGscNGBDNzZdWrhG4j-h48qLua12Hn5q", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4000/assets/css/normalize.css", "1" => "http://localhost:4000/assets/css/tailwind.css", "2" => "http://localhost:4000/assets/app.css", "3" => "http://localhost:4000/assets/css/application.css"}, "vsn" => "2.0.0"}
[debug] MOUNT MonoPhoenixV01Web.SearchbarLive
Parameters: :not_mounted_at_router
Session: %{"_csrf_token" => "hOFWW7UJxSjzQ574LeYYTZU2"}
[debug] Replied in 126µs
Is that :not_mounted_at_router
related to the trouble?
I am not using any slugs in my app yet (or, at least, grepping finds no other instances of āslugā), could I be lacking something that slugs depend on?
Here are my deps:
[
{:phoenix, "~> 1.7.1", override: true},
{:phoenix_ecto, "~> 4.4"},
{:ecto_sql, "~> 3.6"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 3.0"},
{:phoenix_view, "~> 2.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.18.15"},
{:phoenix_live_dashboard, "~> 0.7.2"},
{:esbuild, "~> 0.4", runtime: Mix.env() == :dev},
{:tailwind, "~> 0.1.9", runtime: Mix.env() == :dev},
{:swoosh, "~> 1.3"},
{:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"},
{:gettext, "~> 0.18"},
{:jason, "~> 1.2"},
{:plug_cowboy, "~> 2.5"},
{:redirect, "~> 0.4.0"},
{:html_assertion, "0.1.5", only: :test},
{:floki, ">= 0.30.0", only: :test},
{:credo, "~> 1.6", only: [:dev, :test], runtime: false}
]
Then again, I didnāt see the error until I changed instance of āMonologuesā to āMonofindsā, āMonologueā to āMonofindā, āmonologuesā to āmonofindsā, and āmonologueā to āmonofindā. So, Iām a baffled n00b.
In case itās useful in helping me troubleshoot, below are my current versions of the files from @caspg 's āTravelWebā example. (Please let me know if there are any other files I should paste the contents of. I didnāt include my router.ex
, because youād mentioned thereās no need to add any new routes.)
Iām rendering it in lib/mono_phoenix_v01_web/templates/plays_page/plays.html.heex
, thusly:
<div class="absolute top-24 w-5/6">
<%= live_render(
@conn,
MonoPhoenixV01Web.SearchbarLive,
id: "searchbar",
container:
{:div,
class: "flex flex-wrap justify-between items-start text-left items-center lg:w-full"}
) %>
</div>
lib/mono_phoenix_v01/monofinds/monofinds.ex
:
defmodule MonoPhoenixV01.Monofinds do
@moduledoc """
The search query thingy
"""
import Ecto.Query, warn: false
alias MonoPhoenixV01.Repo
alias MonoPhoenixV01.Monofinds.Monofind
def search(search_query) do
search_query = "%#{search_query}%"
Monofind
|> order_by(asc: :body)
|> where([p], ilike(p.body, ^search_query))
|> limit(15)
|> Repo.all()
end
end
lib/mono_phoenix_v01_web/live/searchbar_live.ex
:
defmodule MonoPhoenixV01Web.SearchbarLive do
use MonoPhoenixV01Web, :live_view
alias Phoenix.LiveView.JS
alias MonoPhoenixV01.Monofinds
def mount(_params, _session, socket) do
socket = assign(socket, monofinds: [])
{:ok, socket, layout: false}
end
def handle_event("change", %{"search" => %{"query" => ""}}, socket) do
socket = assign(socket, :monofinds, [])
{:noreply, socket}
end
def handle_event("change", %{"search" => %{"query" => search_query}}, socket) do
monofinds = Monofinds.search(search_query)
socket = assign(socket, :monofinds, monofinds)
{:noreply, socket}
end
def open_modal(js \\ %JS{}) do
js
|> JS.show(
to: "#searchbox_container",
transition:
{"tw-transition tw-ease-out tw-duration-200", "tw-opacity-0 tw-scale-95",
"tw-opacity-100 tw-scale-100"}
)
|> JS.show(
to: "#searchbar-dialog",
transition: {"tw-transition tw-ease-in tw-duration-100", "tw-opacity-0", "tw-opacity-100"}
)
|> JS.focus(to: "#search-input")
end
def hide_modal(js \\ %JS{}) do
js
|> JS.hide(
to: "#searchbar-searchbox_container",
transition:
{"tw-transition tw-ease-in tw-duration-100", "tw-opacity-100 tw-scale-100",
"tw-opacity-0 tw-scale-95"}
)
|> JS.hide(
to: "#searchbar-dialog",
transition: {"tw-transition tw-ease-in tw-duration-100", "tw-opacity-100", "tw-opacity-0"}
)
end
end
lib/mono_phoenix_v01_web/live/searchbar_live.html.heex
:
<div class="block max-w-96 flex-auto">
<button
type="button"
class="tw-hidden flex items-start text-left text-gray-500 bg-white hover:ring-gray-500 ring-gray-300 h-12 w-full items-center gap-1 rounded-md pl-2 pr-3 text-2xl ring-1 transition lg:flex focus:[&:not(:focus-visible)]:outline-none"
phx-click={open_modal()}
>
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" class="h-7 w-7 stroke-current">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25"
>
</path>
</svg>
Search for monologues...
</button>
</div>
<div
id="searchbar-dialog"
class="hidden fixed inset-0 z-50"
role="dialog"
aria-modal="true"
phx-window-keydown={hide_modal()}
phx-key="escape"
>
<div class="fixed inset-0 bg-zinc-400/25 backdrop-blur-sm opacity-100"></div>
<div class="fixed inset-0 overflow-y-auto px-4 py-4 sm:py-20 sm:px-6 md:py-32 lg:px-8 lg:py-[15vh]">
<div
id="searchbox_container"
class="mx-auto overflow-hidden rounded-lg bg-zinc-50 shadow-xl ring-zinc-900/7.5 md:max-w-xl opacity-100 scale-100"
phx-hook="SearchBar"
>
<div
role="combobox"
aria-haspopup="listbox"
phx-click-away={hide_modal()}
aria-expanded={@monofinds != []}
>
<form action="" novalidate="" role="search" phx-change="change">
<div class="group relative flex h-12">
<svg
viewBox="0 0 20 20"
fill="none"
aria-hidden="true"
class="pointer-events-none absolute left-3 top-0 h-full w-5 stroke-zinc-500"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25"
>
</path>
</svg>
<input
id="search-input"
name="search[query]"
class="flex-auto rounded-lg appearance-none bg-transparent pl-10 text-zinc-900 outline-none focus:outline-none border-slate-200 focus:border-slate-200 focus:ring-0 focus:shadow-none placeholder:text-zinc-500 focus:w-full focus:flex-none md:text-md [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden [&::-webkit-search-results-button]:hidden [&::-webkit-search-results-decoration]:hidden pr-4"
style={
@monofinds != [] &&
"border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom: none"
}
aria-autocomplete="both"
aria-controls="searchbox__results_list"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
enterkeyhint="search"
spellcheck="false"
placeholder="Search for monologues"
type="search"
value=""
tabindex="0"
/>
</div>
<ul
:if={@monofinds != []}
class="divide-y divide-slate-200 overflow-y-auto rounded-b-lg border-t border-slate-200 text-md leading-6"
id="searchbox__results_list"
role="listbox"
>
<%= for monofind <- @monofinds do %>
<li id={"#{monofind.id}"}>
<.link
navigate={~p"/monofinds/#{monofind.slug}"}
class="block p-4 hover:bg-slate-100 focus:outline-none focus:bg-slate-100 focus:text-sky-800"
>
<%= monofind.body %>
</.link>
</li>
<% end %>
</ul>
</form>
</div>
</div>
</div>
</div>
lib/mono_phoenix_v01_web/live/SearchBar.ts
// This is optional phoenix client hook. It allows to use key down and up to select results.
export const SearchBar = {
mounted() {
const searchBarContainer = (this as any).el as HTMLDivElement
document.addEventListener('keydown', (event) => {
if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') {
return
}
const focusElemnt = document.querySelector(':focus') as HTMLElement
if (!focusElemnt) {
return
}
if (!searchBarContainer.contains(focusElemnt)) {
return
}
event.preventDefault()
const tabElements = document.querySelectorAll(
'#search-input, #searchbox__results_list a',
) as NodeListOf<HTMLElement>
const focusIndex = Array.from(tabElements).indexOf(focusElemnt)
const tabElementsCount = tabElements.length - 1
if (event.key === 'ArrowUp') {
tabElements[focusIndex > 0 ? focusIndex - 1 : tabElementsCount].focus()
}
if (event.key === 'ArrowDown') {
tabElements[focusIndex < tabElementsCount ? focusIndex + 1 : 0].focus()
}
})
},
}
Sidenote question on the .ts file: Is focusElemnt
a typo or purposeful/ correct? (Iām guessing the latter, but thought Iād double-check.)
Thanks in advance to @caspg and/ or anyone else willing to help me get unlost!