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.

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"
}
1 Like

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