Here’s my code which is a live component.
defmodule MyAppWeb.SearchFormLive do
use MyAppWeb, :live_component
def mount(socket) do
categories =
MyApp.Categories.list_categories()
|> Enum.map(fn category ->
{category.name, category.slug}
end)
{:ok, assign(socket, categories: categories, temporary_assigns: [caregories: []])}
end
def render(assigns) do
~L"""
<form phx-submit="search" phx-target="<%= @myself %>" phx-hook="SearchSelect">
<input type="hidden" name="url" />
<select id="category" name="category">
<option disabled selected value>Choose a category</option>
<%= options_for_select(@categories, "") %>
</select>
<button type="submit">
Search
</button>
</form>
"""
end
def handle_event("search", %{"category" => category, "url" => url}, socket) do
case String.starts_with?(url, "/search") do
true ->
socket = push_patch(socket, to: Routes.search_path(socket, :category, category))
{:noreply, socket}
false ->
socket = push_redirect(socket, to: Routes.search_path(socket, :category, category))
{:noreply, socket}
end
end
end
And here’s the topbar setup which is the default for live view:
// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", info => topbar.show())
window.addEventListener("phx:page-loading-stop", info => topbar.hide())
The other approach I thought of was to not use a form and to instead just have a link that I manually add the required phx attributes to make it live redirect. Then use a live view hook to update the href based on the selected option i.e.
// live view component render function
def render(assigns) do
<div phx-hook="Find">
<select id="category" name="category">
<option disabled selected value>Choose a category</option>
<%= options_for_select(@categories, "") %>
</select>
<a href="/search" data-phx-link="redirect" data-phx-link-state="push">
<button>Find</button>
</a>
</div>
end
// live view hook
Hooks.Find = {
mounted() {
const selectEl = this.el.querySelector('select')
const aEl = this.el.querySelector('a')
selectEl.addEventListener('change', e => {
aEl.href = `/search/${e.target.value}`
})
}
}