dberg

dberg

Liveview dynamic form - adding items from list

Say I want to make a form for making custom pizzas (the app is not actually about pizzas, but it is an example that is easier to understand).

I have toppings (say cheese, pepper, anchovies, etc) that are shown in a list.
Customers are allowed to create custom pizzas, by giving the pizza a name, and selecting any amount of toppings from a list of toppings that is displayed next to the form.

I have created the following Ecto schemas:

schema "toppings" do
    field :name, :string
  end

schema "pizza_toppings" do
    belongs_to :pizza, Pizza
    belongs_to :topping, Topping
end

schema "pizzas" do
    field :name, :string
    has_many :pizza_toppings, PizzaTopping
    has_many :toppings, through: [:pizza_toppings, :topping]
end

I can not figure out the code that goes in the handle_event("add-topping", %{"topping_id" => topping_id}, socket) function (that is called when clicking on a specific topping) to actually add the topping to the changeset and have it validated by the form. I guess this is due to not quite grokking how changesets work…

I 've tried adapting the guide here One-to-Many LiveView Form | Benjamin Milde but have not succeded.

Most Liked

LostKobrakai

LostKobrakai

Ecto 3.10 (which I hoped to be out by now, but alas it isn’t) will include Ecto.Changeset.get_assoc, which in combination with Ecto.Changeset.put_assoc should make modification of assocs a bit more comfortable.

Is there a reason to go with nested embeds? These make sense if you have multiple fields per row, but you don’t seem to have that. I’d consider using checkboxes to toggle and transform the resulting list to a nested structure only on submit. I’ve a small POC for a checkboxes component and there’s a fly.io blogpost for something similar:
Scratchpad | Benjamin Milde
Making a CheckboxGroup Input · The Phoenix Files

BartOtten

BartOtten

There it is!

https://hexdocs.pm/ecto/changelog.html#v3-10-0-2023-04-10

codeanpeace

codeanpeace

Based off of the guide you’re trying to adapt and your join schema, maybe try something like this?

def handle_event("add-topping", %{"topping_id" => topping_id}, socket) do
  pizza_topping = %PizzaTopping{topping_id: topping_id}

  socket =
    update(socket, :form, fn %{source: changeset} ->
      existing = get_change_or_field(changeset, :pizza_toppings)
      changeset = Ecto.Changeset.put_assoc(changeset, :pizza_toppings, existing ++ [pizza_topping])
      to_form(changeset)
    end)

  {:noreply, socket}
end

defp get_change_or_field(changeset, field) do
  with nil <- Ecto.Changeset.get_change(changeset, field) do
    Ecto.Changeset.get_field(changeset, field, [])
  end
end

See: Adding tags to a post | Ecto.Changeset

There seems to be an understandable disconnect between Ecto and LiveView that likely exists because Ecto predates LiveView and was designed for the stateless paradigm of HTTP requests handled by conventional controllers. Subsequently, there is no default interface/function to incrementally build/add to an existing association within a parent changeset. Basically, something like Ecto.Changeset.add_to_assoc/3 doesn’t exist – but if it did, we could then do something like this:

def handle_event("add-topping", %{"topping_id" => topping_id}, socket) do
   {:noreply,
     update(socket, :form, fn %{source: changeset} ->
       changeset
       |> Ecto.Changeset.add_to_assoc(:pizza_toppings, %PizzaTopping{topping_id: topping_id})
       |> to_form
     end)
   }
end
dberg

dberg

Yes, I’m starting to think that might be good way of implementing it. Thanks!

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
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
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
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
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
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
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

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 th...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
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
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
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
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
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
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
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

We're in Beta

About us Mission Statement