I’ve just been using a search form and use the phx-submit event on the form to run the search. To do this, you need a form-ready data structure (i.e something that implements Phoenix.HTML.FormData protocol) - I used an Ecto embedded schema - and then use the form content to build the search query. Most essential parts of the code are below.
Template (index.html.leex)
<%= if !@search_hidden do %>
<section class="section">
<%= form_for @searchset, "", [phx_submit: :search_events], fn f -> %>
<%= input f, :from_date %>
<%= input f, :to_date %>
<%= input f, :reported_by %>
<%= input f, :searchtext %>
<div class="buttons">
<%= submit "Search", phx_disable_with: "Searching...", class: "button" %>
</div>
<% end %>
</section>
<% end %>
# Render search results here (in my case, @events contains the search results)
Embedded ecto schema representing the search criteria so that the form “just works”
defmodule ThingWeb.EventSearch
use Ecto.Schema
import Ecto.Changeset
embedded_schema do
field :from_date, :date
field :to_date, :date
field :reported_by, :string
field :searchtext, :string
end
def changeset(eventsearch, attrs \\ %{}) do
eventsearch
|> cast(attrs, [:from_date, :to_date, :reported_by, :searchtext])
end
end
and then in my liveview controller it’s something along the lines of:
def mount(_session, socket) do
# Create default search params when mounting the liveview
searchset =
EventSearch.changeset(%EventSearch{})
|> Ecto.Changeset.put_change(:from_date, Date.add(Date.utc_today, -180))
|> Ecto.Changeset.put_change(:to_date, Date.utc_today)
# Run the search for the default search parameters - you may not want to do this bit until the use clicks search
{:ok, fetch(socket, searchset)}
end
# Render using the index.html.leex template above
def render(assigns), do: EventView.render("index.html", assigns)
# Handle the phx-submit event (index.html.leex maps phx-submit to "search_events")
def handle_event("search_events", %{"event_search" => params}, socket) do
# Cast form params into nice struct
searchset =
%EventSearch{}
|> EventSearch.changeset(params)
{:noreply, fetch(socket, searchset)}
end
# Run the search, populate the @events assign and "remember" the search params so we can redraw the search form
defp fetch(socket, searchset) do
assign(socket, events: search_events(searchset), searchset: searchset)
end
# The actual search logic - takes the ecto embedded schema with the search params, generates
# the search query and returns the query results
defp search_events(searchset) do
# Can't remember why I did this but it's probably important
eventsearch = Ecto.Changeset.apply_changes(searchset)
# Create your query however you need to
query = from e in Event,
select: e
query = query
|> add_date_range_search(eventsearch)
|> add_reported_by_search(eventsearch)
|> add_searchtext(eventsearch)
# Return the search results
Thing.Repo.all(query)
end
I think that’s about it… I’m sure others have better ways to do liveview based searches, but this works well for me.