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

nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
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
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: &lt;h1&gt;Create Post&lt;/h1&gt; &lt;%= ...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
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

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39467 209
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
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
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement