Checkbox binding in LiveView

I am trying to bind a checkbox to a property in my assigns. My very basic LiveView 0.16.0 module looks like this:

defmodule CheckboxWeb.Live.PlaygroundLive do
  use CheckboxWeb, :live_view

  def mount(_, _, socket) do
    {:ok, assign(socket, counter: 0, is_selected: true)}
  end

  def render(assigns) do
    ~H"""
    <input type="checkbox" checked={@is_selected} phx-click="toggle">
    """
  end

  def handle_event("toggle", _, %{assigns: %{counter: counter}} = socket) do
    {:noreply, assign(socket, counter: counter + 1)}
  end
end

I would expect the checkbox to stay checked after the server response, however the check box toggles on and off. When I add something like data-counter={@counter} to the input element, it behaves as I expect.

Very new to Elixir, Phoenix, and LiveView and feel like I’m missing something really basic here, but have not had much luck figuring out what it is.

1 Like

I’d recommend using Phoenix.HTML.Form - checkbox

Looks like you do not want to create a form for a changeset, so look at “With limited data” and “Without form data” here: Phoenix.HTML.Form — Phoenix.HTML v3.0.4

Example:

<form action="" phx-change="my_form_changed">
  <%= Phoenix.HTML.Form.checkbox(:my_checkboxes, "my_first_checkbox",
    checked_value: "1",
    unchecked_value: "0",
    value: @my_checkboxes_values.my_first_checkbox
  )
  %>
  <%= Phoenix.HTML.Form.checkbox(:my_checkboxes, "my_second_checkbox",
    checked_value: "1",
    unchecked_value: "0",
    value: @my_checkboxes_values.my_second_checkbox
  )
  %>
</form>

...
# assign
my_checkboxes_values: %{my_first_checkbox: "0", my_second_checkbox: "0"}
...

@impl true
def handle_event("my_form_changed", params, socket) do
  %{"_target" => ["my_checkboxes", ref], "my_checkboxes" => map} = params
  IO.inspect({ref, map, Map.get(map, ref)}, label: :my_form_changed)
  # update my_checkboxes_values assign
  {:noreply, socket}
end

will output:

my_form_changed: {
 "my_first_checkbox",
 %{"my_first_checkbox" => "1", "my_second_checkbox" => "0"}, 
 "1"
}
3 Likes

When you click the checkbox, the browser toggles it, but since you bound click event handler, after the checkbox is toggled by the browser - liveview processes the event and rerenders your template using your old @is_checked value (which didn’t change).

1 Like