Idiomatic approach to building a custom form component for button-groups

Hi.

I’m trying to create a custom input component and can’t figure out the right way to do it.

The component I’m trying to build is a dynamic button-group selection. Something like below where there’s a dynamic list of criteria, and for each criteria, the user can select a number from 1-3. It’s essentially a stylized dynamic list of radio button groups.

Ideally I’d like the component to work and behave like other input types so that I can put it into a standard form and receive the value on form submit/change. I’m thinking that the form could output a map like %{criteria_id => value, ...}.

The approach i’ve taken at the moment is to generate the view, and each button click triggers an event which must then be handled in the liveview. The handler updates an internal assigns, and passes it back through to the component to set some hidden fields.

E.G, in the liveview, I have something like the following

  @impl true
  def handle_event(
        "set-criteria-rating",
        %{"criteria-id" => criteria_id, "rating" => rating},
        socket
      ) do
    {rating, _} = Integer.parse(rating)
    socket = update(socket, :criteria_ratings, &Map.put(&1, criteria_id, rating))
    {:noreply, socket}
  end

And this is passed back down into the component later

  <.criteria_input
    criterias={@criterias}
    criteria_ratings={@criteria_ratings}
  />

Is there a simpler or more idiomatic way to achieve this?

I’d probably do radio inputs per criteria with input_name[criteria] and some css on the labels to make things look like they should look.

1 Like