I have a livebook cell that starts with a single input, then appends additional select inputs recursively based on the input of the previous select box. It’s a self-referencing table, so it’s using parent / child relations to populate the next box. I’m pretty surprised that this works as I thought the recursive Kino.listen/2
calls would break something, but pleasingly it works pretty well.
The obvious problem is that inputs are only appended, not removed. If an earlier selection in the list is changed, another box is simply appended on the end. I can’t find any obvious way to track inputs appended to a frame so I can remove them without clearing the whole form - is there any way to track these? I have also tried using a Kino form or just adding raw inputs but neither seems to allow an append like the frame does.
defmodule AreaInputs do
import Ecto.Query
defp parent_id_filter(queryable, parent_id) do
case parent_id do
nil -> queryable |> where([a], is_nil(a.parent_id))
_ -> queryable |> where([a], a.parent_id == ^parent_id)
end
end
def get_areas(parent_id) do
Area
|> parent_id_filter(parent_id)
|> order_by([a], a.name)
|> AreaRepo.all()
|> Enum.map(fn area -> {area.id, area.name} end)
end
def append_select_and_listen(frame, event) do
new_input = Kino.Input.select("", AreaInputs.get_areas(event.value))
Kino.Frame.append(frame, new_input)
Kino.listen(new_input, fn event_2 ->
append_select_and_listen(frame, event_2)
end)
end
end
frame = Kino.Frame.new() |> Kino.render()
initial_input = Kino.Input.select("", AreaInputs.get_areas(nil))
Kino.Frame.render(frame, initial_input)
Kino.listen(initial_input, fn event ->
AreaInputs.append_select_and_listen(frame, event)
end)