I have a paginated datatable for products and I want users to be able to filter the product price
from a min
price to a max
price.
This is the code in my products context:
def list_products(criteria) when is_list(criteria) do
query = from (r in Product), order_by: [desc: r.inserted_at]
Enum.reduce(criteria, query, fn
{:paginate, %{page: page, per_page: per_page}}, query ->
from q in query,
offset: ^((page - 1) * per_page),
limit: ^per_page
{:filter, %{min: nil, max: nil}}, query ->
query
{:filter, %{min: "", max: ""}}, query ->
query
{:filter, %{min: min, max: max}}, query ->
from q in query,
where: q.price >= ^min and q.price <= ^max
end)
|> Repo.all()
|> Repo.preload(:user)
end
Liveview:
def handle_params(params, _url, socket) do
page = String.to_integer(params["page"] || "1")
per_page = String.to_integer(params["per_page"] || "5")
paginate_options = %{page: page, per_page: per_page}
filter_options = %{min: nil, max: nil}
products = Products.list_products(paginate: paginate_options, filter: filter_options)
socket =
assign(socket,
options: paginate_options,
filter: filter_options,
products: products
)
{:noreply, socket}
end
def handle_event("filter", %{"min" => min, "max" => max}, socket) do
filter_options = %{min: min, max: max}
products = Products.list_products(paginate: socket.assigns.options, filter: filter_options)
socket =
push_patch(socket,
to:
Routes.products_index_path(
socket,
:index,
products: products,
page: socket.assigns.options.page,
per_page: socket.assigns.options.per_page,
min: min,
max: max
)
)
{:noreply, socket}
end
I am not sure how to combine the pagination feature with the filtering feature.
Currently with the above code, I can get the filtering parameters appended in the URL but using push_patch
I don’t get the datatable updated only when using assign
which doesn’t append the filtering parameters in the URL.