Phoenix_live_view: Forms in comprehensions are broken

Created on 28 Apr 2019  Â·  8Comments  Â·  Source: phoenixframework/phoenix_live_view

Environment

  • Elixir version (elixir -v): Erlang/OTP 21 [erts-10.0.4] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [hipe] Elixir 1.7.1 (compiled with Erlang/OTP 21)
  • Phoenix version (mix deps): 1.4.3
  • NodeJS version (node -v): v10.15.3
  • NPM version (npm -v): 6.4.1
  • Operating system: macOS 10.14.4

Actual behavior

When rendering a <form> inside comprehensions it appears too often and triggering events does result in even more <form> tags to be rendered:

broken mov

You can see the error for yourself here: https://broken-live-view.herokuapp.com
The source code is available here: https://github.com/ream88/broken_live_view

Expected behavior

Each <form> should only be rendered once.

Most helpful comment

just wanted to say that adding unique id attrs to HTML tags will solve the issue. thanks @chrismccord and @snewcomer for stopping me from tearing out my hair! :)

All 8 comments

I tried debugging this and right now I'm thinking it's actually a bug in morphdom. The inputs to the morphdom call are correct on the LiveView side, but the resulting DOM structure contains duplicates.

I think I'm running into the same issue… any word on progress on this bug?

@snewcomer I'd love your help on this one since @wrren's digging seems like a morphdom issue, but I will try to recreate and verify on the Elixir side to rule out an issue on our side. Comprehensions are treated specially so it's possible we aren't building the correct HTML before passing off to morphdom

I think I had a similar issue; I seem to have fixed it by adding a unique as: attribute for each form:

    <%= for event_cs <- @events do %>
      <%= f = form_for event_cs, "#", [phx_change: :validate, class: "row", as: "event#{event_cs.data.id}"] %>
…

Then, in my validate handler I broke apart the params I was getting to find the right key:

  def handle_event("validate", params, socket) do
    "event" <> event_id =
      Map.keys(params)
      |> Enum.find(fn i -> Regex.match?(~r/^event(?:\d*)$/, i) end)
    …
    {:noreply, socket}
  end

Feels like a bit of a kluge, but it seems to work for now. ¯\_(ツ)_/¯ Do you think we need to add something like a key to each element rendered by a comprehension? (Kind of like how React, Vue, etc. do?)

I run into similar issue as well. When I inserted elements into an assign from which a for comprehension was rendering forms, the inputs in forms were not rendered correctly. @ashton314 suggestion to add as option made it work as intended.

@lukaszsamson This is a common issue we have come across and have learned (seems you have too) that if you are inside a loop, we need to make sure 1. No duplicate id attributes 2. but also that elements can be identified clearly by the diffing algorithm.

So in the broken_live_view repo above <input type="text" name="text" value="<%= text %>" id="text-<%= id %>" /> will solve the issue.

Scott is correct here. Duplicate IDs cannot be diffed properly and it's not something we can handle on our side because IDs are supposed to be unique. Hope that helps. Thanks!

just wanted to say that adding unique id attrs to HTML tags will solve the issue. thanks @chrismccord and @snewcomer for stopping me from tearing out my hair! :)

Was this page helpful?
0 / 5 - 0 ratings