Fl4m3Ph03n1x

Fl4m3Ph03n1x

Why clicking a checkbox fires a Phoenix LiveView event?

Background

I have a form that has some checkboxes. This form will show checkboxes depending on a condition.
After the form I have a button, which is supposed to send a phoenix event that will perform an action.

However, when I click a checkbox (to select/de-select it) the phoenix event is still created.
I honestly don’t know why, I have a feeling this is related to the fact that I am not using Phoenix forms (I am using pure HTML ones, which are less safe) but I don’t have enough experience to know.

Code

@spec render(map) :: Rendered.t
def render(assigns) do
  ~H"""  
  <div class="body">
    <form phx-change="filters">
      <div>
        <input type="hidden" name="syndicates[]" value="">
        <%= for synd <- @syndicates  do %>
          <%= syndicate_checkbox(synd: synd, checked: synd in @selected_syndicates) %>
        <% end %>
      </div>
    </form>

    <div class={@selected_syndicates |> none_active?() |> display()}>
      <p>Nothing to see here!.</p>
    </div>

    <button
      class={@selected_syndicates |> any_active?() |> display()}
      phx-click="execute_command"
      phx-value-command={@selected_command.id}
      phx-value-strategy={@selected_strategy.id}>
        Execute Command
    </button>

  </div>
  """
end

  @spec display(boolean) :: String.t()
  defp display(true), do: "show"
  defp display(_), do: "hidden"

  @spec any_active?([map]) :: boolean
  defp any_active?([]), do: false
  defp any_active?(_), do: true

  @spec none_active?([map]) :: boolean
  defp none_active?(data), do: !any_active(data)

  @spec syndicate_checkbox(map) :: Rendered.t
  defp syndicate_checkbox(assigns) do
    assigns = Enum.into(assigns, %{})

    ~H"""
      <div class={display(@checked)}>
        <div class="row single-syndicate">
          <input class="column single-checkbox" type="checkbox" id="{@synd.id}"
                  name="syndicates[]" value="{@synd.id}">

          <label for="{@synd.id}" class="column"><%= @synd.name %></label>
        </div>
      </div>
    """
  end

Problem

Basically, my objective here is simple:

  • if none_active? returns true, then I show a text and I hide the button
  • otherwise, I show the button and hide the text

However, the behavior displayed is very confusing to me (see Deactivate menu):

ezgif.com-gif-maker(1)

(For a bigger image see https://i.stack.imgur.com/9ThDu.gif)

The error shown in the console behind is:

11:09:28.334 [error] GenServer #PID<0.2115.0> terminating
** (FunctionClauseError) no function clause matching in WebInterface.Live.Window.handle_event/3
    (web_interface 1.1.0) lib/web_interface/live/window.ex:53: WebInterface.Live.Window.handle_event("filters", %{"_target" => ["syndicates"], "syndicates" => ["", "{@synd.id}"]}, #Phoenix.LiveView.Socket<assigns: %{__changed__: %{}, commands: [%{description: "\n          Activating a syndicate will cause the app to create a sell order on warframe.market for each product of the said syndicate.\n          The prices of each item will be determined accoring to a strategy that you can define.\n        ", id: :activate, name: "Activate"}, %{description: "\n          Deactivating a syndicate removes all sell orders from waframe.market for the given syndicate.\n        ", id: :deactivate, name: "Deactivate"}, %{description: "\n          Saving authentication information will allow this application to make requests in your behalf.\n          It is a required step for the application to work.\n        ", id: :authenticate, name: "Authenticate"}], flash: %{"info" => "Request completed: [ok: :success]"}, live_action: nil, selected_command: %{description: "\n          Deactivating a syndicate removes all sell orders from waframe.market for the given syndicate.\n        ", id: :deactivate, name: "Deactivate"}, selected_strategy: %{description: "\n          Gets the 3 lowest prices for the given item and calculates the average.\n        ", id: :top_three_average, name: "Top 3 Average"}, selected_syndicates: [%{id: "red_veil", name: "Red Veil"}], strategies: [%{description: "\n          Gets the 3 lowest prices for the given item and calculates the average.\n        ", id: :top_three_average, name: "Top 3 Average"}, %{description: "\n          Gets the 5 lowest prices for the given item and calculates the average.\n        ", id: :top_five_average, name: "Top 5 Average"}, %{description: "\n          Gets the lowest price for the given item and uses it.\n        ", id: :equal_to_lowest, name: "Equal to lowest"}, %{description: "\n          Gets the lowest price for the given item and beats it by 1.\n        ", id: :lowest_minus_one, name: "Lowest minus one"}], syndicates: [%{id: "red_veil", name: "Red Veil"}, %{id: "perrin_sequence", name: "Perrin Sequence"}, %{id: "new_loka", name: "New Loka"}, %{id: "arbiters_of_hexis", name: "Arbiters of Hexis"}, %{id: "steel_meridian", name: "Steel Meridian"}, %{id: "cephalon_suda", name: "Cephalon Suda"}, %{id: "simaris", name: "Cephalon Simaris"}]}, endpoint: WebInterface.Endpoint, id: "phx-FuUYGcft5EwElQBl", parent_pid: nil, root_pid: #PID<0.2115.0>, router: WebInterface.Router, transport_pid: #PID<0.2084.0>, view: WebInterface.Live.Window, ...>)
    (phoenix_live_view 0.17.6) lib/phoenix_live_view/channel.ex:349: anonymous fn/3 in Phoenix.LiveView.Channel.view_handle_event/3
    (telemetry 1.0.0) c:/Users/palme/Worskapce/fl4m3/market_manager/deps/telemetry/src/telemetry.erl:293: :telemetry.span/3
    (phoenix_live_view 0.17.6) lib/phoenix_live_view/channel.ex:206: Phoenix.LiveView.Channel.handle_info/2
    (stdlib 3.17.1) gen_server.erl:695: :gen_server.try_dispatch/4
    (stdlib 3.17.1) gen_server.erl:771: :gen_server.handle_msg/6
    (stdlib 3.17.1) proc_lib.erl:236: :proc_lib.wake_up/3
Last message: %Phoenix.Socket.Message{event: "event", join_ref: "37", payload: %{"event" => "filters", "type" => "form", "uploads" => %{}, "value" => "syndicates%5B%5D=&syndicates%5B%5D=%7B%40synd.id%7D&_target=syndicates%5B%5D"}, ref: "151", topic: "lv:phx-FuUYGcft5EwElQBl"}
State: %{components: {%{}, %{}, 1}, join_ref: "37", serializer: Phoenix.Socket.V2.JSONSerializer, socket: #Phoenix.LiveView.Socket<assigns: %{__changed__: %{}, commands: [%{description: "\n          Activating a syndicate will cause the app to create a sell order on warframe.market for each product of the said syndicate.\n          The prices of each item will be determined accoring to a strategy that you can define.\n        ", id: :activate, name: "Activate"}, %{description: "\n          Deactivating a syndicate removes all sell orders from waframe.market for the given syndicate.\n        ", id: :deactivate, name: "Deactivate"}, %{description: "\n          Saving authentication information will allow this application to make requests in your behalf.\n          It is a required step for the application to work.\n        ", id: :authenticate, name: "Authenticate"}], flash: %{"info" => "Request completed: [ok: :success]"}, live_action: nil, selected_command: %{description: "\n          Deactivating a syndicate removes all sell orders from waframe.market for the given syndicate.\n        ", id: :deactivate, name: "Deactivate"}, selected_strategy: %{description: "\n          Gets the 3 lowest prices for the given item and calculates the average.\n        ", id: :top_three_average, name: "Top 3 Average"}, selected_syndicates: [%{id: "red_veil", name: "Red Veil"}], strategies: [%{description: "\n          Gets the 3 lowest prices for the given item and calculates the average.\n        ", id: :top_three_average, name: "Top 3 Average"}, %{description: "\n          Gets the 5 lowest prices for the given item and calculates the average.\n        ", id: :top_five_average, name: "Top 5 Average"}, %{description: "\n          Gets the lowest price for the given item and uses it.\n        ", id: :equal_to_lowest, name: "Equal to lowest"}, %{description: "\n          Gets the lowest price for the given item and beats it by 1.\n        ", id: :lowest_minus_one, name: "Lowest minus one"}], syndicates: [%{id: "red_veil", name: "Red Veil"}, %{id: "perrin_sequence", name: "Perrin Sequence"}, %{id: "new_loka", name: "New Loka"}, %{id: "arbiters_of_hexis", name: "Arbiters of Hexis"}, %{id: "steel_meridian", name: "Steel Meridian"}, %{id: "cephalon_suda", name: "Cephalon Suda"}, %{id: "simaris", name: "Cephalon Simaris"}]}, endpoint: WebInterface.Endpoint, id: "phx-FuUYGcft5EwElQBl", parent_pid: nil, root_pid: #PID<0.2115.0>, router: WebInterface.Router, transport_pid: #PID<0.2084.0>, view: WebInterface.Live.Window, ...>, topic: "lv:phx-FuUYGcft5EwElQBl", upload_names: %{}, upload_pids: %{}}

Question

As far as I understand, the crash happens because I am sending a filter event that I am not catching in a higher level.

However, I don’t understand why this event is being generated in the first place. I am only selecting/deselecting a checkbox, nothing should be happening.

  • What am I doing wrong?
  • Should I replace this HTML form with a Phoenix one? If so, would I go about it?

Marked As Solved

tfwright

tfwright

The filters event is being triggered when you check the box because that is a form change, and you have set filters as the value for phx-change on the form.

The error is because you have not correctly defined the handle_event function on the live view controlling the form.

Also Liked

LostKobrakai

LostKobrakai

You can add a submit button to the form and use phx-submit instead of phx-change. Then you’ll only be sent an event if the form is submitted.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

That said, I highly recommend a phx-change event since it is usually pretty critical to keep the form state in sync front end to backend.

Where Next?

Popular in Questions Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call t...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43757 214
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement