Last active
October 14, 2024 15:16
-
-
Save LostKobrakai/ce5385bd118189a24d60893188612de9 to your computer and use it in GitHub Desktop.
Phoenix LiveView form with nested embeds and add/delete buttons
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule NestedWeb.FormLive do | |
use NestedWeb, :live_view | |
require Logger | |
defmodule Form do | |
use Ecto.Schema | |
import Ecto.Changeset | |
embedded_schema do | |
field :name, :string | |
embeds_many :cities, City, on_replace: :delete do | |
field :name, :string | |
end | |
end | |
def changeset(form, params) do | |
form | |
|> cast(params, [:name]) | |
|> validate_required([:name]) | |
# When string "[]" is detected, make it an empty list | |
# Doing that after the cast on `changeset.params` guarantees string keys | |
# Only works if `cast/4` is used though, which should be the case with forms | |
|> then(fn changeset -> | |
if changeset.params["cities"] == "[]" do | |
Map.update!(changeset, :params, &Map.put(&1, "cities", [])) | |
else | |
changeset | |
end | |
end) | |
|> cast_embed(:cities, with: &city_changeset/2) | |
end | |
def city_changeset(city, params) do | |
city | |
|> cast(params, [:name]) | |
|> validate_required([:name]) | |
end | |
end | |
def render(assigns) do | |
~H""" | |
<.form for={@changeset} let={f} phx-change="validate" phx-submit="submit"> | |
<%= label f, :name %> | |
<%= text_input f, :name %> | |
<%= error_tag f, :name %> | |
<fieldset> | |
<legend>Cities</legend> | |
<%# Hidden input will make sure "cities" is a key in `params` map for no cities to persist %> | |
<%# Needs to be before `inputs_for` to not overwrite cities if present %> | |
<%= hidden_input f, :cities, value: "[]" %> | |
<%= for f_city <- inputs_for(f, :cities) do %> | |
<div> | |
<%= hidden_inputs_for(f_city) %> | |
<%= label f_city, :name %> | |
<%= text_input f_city, :name %> | |
<%= error_tag f_city, :name %> | |
<button type="button" phx-click="delete-city" phx-value-index={f_city.index}>Delete</button> | |
</div> | |
<% end %> | |
<button type="button" phx-click="add-city">Add</button> | |
</fieldset> | |
<%= submit "Submit" %> | |
</.form> | |
""" | |
end | |
def mount(_, _, socket) do | |
base = %Form{ | |
id: "4e4d0944-60b3-4a09-a075-008a94ce9b9e", | |
name: "Somebody", | |
cities: [ | |
%Form.City{ | |
id: "26d59961-3b19-4602-b40c-77a0703cedb5", | |
name: "Berlin" | |
}, | |
%Form.City{ | |
id: "330a8f72-3fb1-4352-acf2-d871803cd152", | |
name: "Singapour" | |
} | |
] | |
} | |
changeset = Form.changeset(base, %{}) | |
{:ok, assign(socket, base: base, changeset: changeset)} | |
end | |
def handle_event("add-city", _, socket) do | |
socket = | |
update(socket, :changeset, fn changeset -> | |
existing = Ecto.Changeset.get_field(changeset, :cities, []) | |
Ecto.Changeset.put_embed(changeset, :cities, existing ++ [%{}]) | |
end) | |
{:noreply, socket} | |
end | |
def handle_event("delete-city", %{"index" => index}, socket) do | |
index = String.to_integer(index) | |
socket = | |
update(socket, :changeset, fn changeset -> | |
existing = Ecto.Changeset.get_field(changeset, :cities, []) | |
Ecto.Changeset.put_embed(changeset, :cities, List.delete_at(existing, index)) | |
end) | |
{:noreply, socket} | |
end | |
def handle_event("validate", %{"form" => params}, socket) do | |
changeset = | |
socket.assigns.base | |
|> Form.changeset(params) | |
|> struct!(action: :validate) | |
{:noreply, assign(socket, changeset: changeset)} | |
end | |
def handle_event("submit", %{"form" => params}, socket) do | |
changeset = Form.changeset(socket.assigns.base, params) | |
case Ecto.Changeset.apply_action(changeset, :insert) do | |
{:ok, data} -> | |
Logger.info("Submitted the following data: \n#{inspect(data, pretty: true)}") | |
socket = put_flash(socket, :info, "Submitted successfully") | |
{:noreply, assign(socket, changeset: changeset)} | |
{:error, changeset} -> | |
{:noreply, assign(socket, changeset: changeset)} | |
end | |
end | |
end |
@dennym Are you asking which function it is? https://hexdocs.pm/phoenix_live_view/0.19.3/Phoenix.Component.html#update/3
@dennym Are you asking which function it is? https://hexdocs.pm/phoenix_live_view/0.19.3/Phoenix.Component.html#update/3
Thank you very much. Sometimes its hard to tell where some functions come from...
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What is
update
on #L105 ?