LiveView with stream how to filter for more than one field?


I have a liveview which presents data from a query in a .table from the CoreComponents.
I want to filter the data, and for that I added a .simple_form on top with three different search criteria, using .input
and px-change for “real-time” filter update.

Filtering itself works, the correct data is displayed.
However, when I want to filter for more than one field, and jump to another field, as soon as I enter data in the second .input, the first one is cleared.
To update the data, I create a new query and use stream with reset: true.

The data for the .simple_form is created from a map with to_form.

    |> assign(:filter, Phoenix.Component.to_form(%{
               "test":     %{"type": "text", "name": "test",     "id": "test",     "value": "ts"},
               "operator": %{"type": "text", "name": "operator", "id": "operator", "value": ""},
               "verdict":  %{"type": "text", "name": "verdict",  "id": "verdict",  "value": ""}

If I inspect socket.assigns.filter
at runtime, I never see the current data from the search form.

<.simple_form for={@filter} phx-change="filtering">
<div class="grid grid-cols-4 gap-4 place-content-center">
  <.input field={@filter[:test]}     label="Test"     type="text"/>
  <.input field={@filter[:operator]} label="Operator" type="text"/>
  <.input field={@filter[:verdict]}  label="Verdict"  type="text"/>

<.table id="results" rows={@streams.results}
        row_click={fn {_id, result} -> JS.navigate(~p"/results/#{result}") end}
  <:col :let={{_id, result}} label="Test">      <%= result.test %>      </:col>
  <:col :let={{_id, result}} label="Operator">  <%= result.operator %>  </:col>
  <:col :let={{_id, result}} label="Started at"><%= result.started_at %></:col>
  <:col :let={{_id, result}} label="Ended at">  <%= result.ended_at %>  </:col>
  <:col :let={{_id, result}} label="Verdict">   <%= result.passed %>    </:col>
  <:action :let={{id,result}}>
    <div class="sr-only">
      <.link navigate={~p"/results/#{result}"}>Show</.link>
  <:action :let={{id,result}}>
    <div class="sr-only">
      <.link navigate={~p"/results/#{result}/pdf"}>PDF</.link>

I was up to now also not able to show some initial data in the form with .to_form and a map.
If someone would have an example for this usecase (initializing a form from a map), that would help me a lot.

Hopefully its something obvious for a more experienced phoenix user than me…

I think it has something to do with the stream reset.
If I change the data to a regular assign with a list, the entries in the form are not cleared.

Final update, I found a solution which works for me.

Initially, I bind the .simple_form’s fields with a map and to_form()
It seems that this information is not updated later, at least inspecting the socket
always shows the initial values for the Form struct.

Anyway, when the handle_event from a phx-change is called, I take the current values
of the form data and update the respective @form assign with to_form.

I put the whole stuff into a small demo repo, you can have a look at it here:

One thing I noticed about your steam implementation was that it could benefit from some info found in the documentation:

Required DOM attributes

For stream items to be trackable on the client, the following requirements must be met:

  1. The parent DOM container must include a phx-update="stream"attribute, along with a unique DOM id.
  2. Each stream item must include its DOM id on the item’s element.

Note: Failing to place phx-update="stream" on the immediate parent for each stream will result in broken behavior.