Created
June 19, 2024 01:48
-
-
Save nallwhy/d0ec48edcfff5a4d34bf5124ef010b63 to your computer and use it in GitHub Desktop.
manage_relationship treat create before destroy
This file contains hidden or 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
Mix.install( | |
[ | |
{:plug_cowboy, "~> 2.7"}, | |
{:jason, "~> 1.0"}, | |
{:phoenix, "1.7.12"}, | |
{:phoenix_live_view, "0.20.14"}, | |
{:ash, "3.0.13"}, | |
{:ash_phoenix, "2.0.4"} | |
], | |
config: [ | |
sample: [ | |
{SamplePhoenix.Endpoint, | |
[ | |
http: [ip: {127, 0, 0, 1}, port: 5001], | |
server: true, | |
live_view: [signing_salt: "aaaaaaaa"], | |
secret_key_base: String.duplicate("a", 64) | |
]} | |
], | |
ash: [require_atomic_by_default?: false] | |
] | |
) | |
defmodule SamplePhoenix.ErrorView do | |
def render(template, _), do: Phoenix.Controller.status_message_from_template(template) | |
end | |
defmodule Author do | |
use Ash.Resource, data_layer: Ash.DataLayer.Ets, domain: Domain | |
attributes do | |
uuid_primary_key :id | |
attribute :name, :string, allow_nil?: false | |
end | |
actions do | |
defaults [:read, :destroy, create: [:name], update: [:name]] | |
end | |
relationships do | |
belongs_to :post, Post | |
end | |
identities do | |
identity :post_id_name, [:post_id, :name], pre_check?: true | |
end | |
end | |
defmodule Post do | |
use Ash.Resource, data_layer: Ash.DataLayer.Ets, domain: Domain | |
attributes do | |
uuid_primary_key :id | |
attribute :title, :string, allow_nil?: false | |
attribute :content, :string, allow_nil?: false | |
end | |
actions do | |
defaults [:read, :destroy] | |
create :create do | |
primary? true | |
accept [:title, :content] | |
argument :authors, {:array, :map} | |
change manage_relationship(:authors, type: :create) | |
end | |
update :update do | |
primary? true | |
accept [:title, :content] | |
argument :authors, {:array, :map} | |
change manage_relationship(:authors, type: :direct_control) | |
end | |
end | |
relationships do | |
has_many :authors, Author | |
end | |
end | |
defmodule Domain do | |
use Ash.Domain | |
resources do | |
resource Post | |
resource Author | |
end | |
end | |
defmodule SamplePhoenix.SampleLive do | |
use Phoenix.LiveView, layout: {__MODULE__, :live} | |
alias Phoenix.LiveView.JS | |
@impl true | |
def mount(_params, _session, socket) do | |
post = | |
Post | |
|> Ash.Changeset.for_create(:create, %{ | |
title: "title", | |
content: "content", | |
authors: [%{name: "author1"}] | |
}) | |
|> Ash.create!() | |
form = | |
post | |
|> AshPhoenix.Form.for_update(:update, forms: [auto?: true]) | |
|> to_form() | |
socket = | |
socket | |
|> assign(form: form) | |
|> assign(result: nil) | |
{:ok, socket} | |
end | |
# layout | |
@impl true | |
def render("live.html", assigns) do | |
~H""" | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/priv/static/phoenix.min.js"> | |
</script> | |
<script | |
src="https://cdn.jsdelivr.net/npm/[email protected]/priv/static/phoenix_live_view.min.js" | |
> | |
</script> | |
<script> | |
let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket) | |
liveSocket.connect() | |
</script> | |
<style> | |
* { font-size: 1.1em; } | |
</style> | |
<%= @inner_content %> | |
""" | |
end | |
# render | |
@impl true | |
def render(assigns) do | |
~H""" | |
<.form id="form" for={@form} phx-change="validate" phx-submit="save"> | |
<div> | |
<div> | |
<label for={@form[:title].name}>title</label> | |
<input | |
type="text" | |
id={@form[:title].id} | |
name={@form[:title].name} | |
value={@form[:title].value} | |
/> | |
</div> | |
<div> | |
<label for={@form[:content].name}>content</label> | |
<input | |
type="text" | |
id={@form[:content].id} | |
name={@form[:content].name} | |
value={@form[:content].value} | |
/> | |
</div> | |
<.inputs_for :let={fa} field={@form[:authors]}> | |
<div> | |
<label for={fa[:name].name}>name</label> | |
<input type="text" id={fa[:name].id} name={fa[:name].name} value={fa[:name].value} /> | |
<button type="button" phx-click={JS.push("remove_author", value: %{"path" => fa.name})}> | |
Remove author | |
</button> | |
</div> | |
</.inputs_for> | |
<div> | |
<button type="button" phx-click="add_author">Add author</button> | |
</div> | |
</div> | |
<button type="submit" disabled={[email protected]?}>Save</button> | |
</.form> | |
<div :if={@form.source.source.errors |> Enum.any?()}> | |
<%= inspect(@form.source.source.errors) %> | |
</div> | |
<div :if={@result}> | |
<%= inspect(@result) %> | |
</div> | |
<%= inspect(@form) %> | |
""" | |
end | |
@impl true | |
def handle_event("validate", %{"form" => params}, socket) do | |
new_form = socket.assigns.form |> AshPhoenix.Form.validate(params) | |
socket = | |
socket | |
|> assign(form: new_form) | |
{:noreply, socket} | |
end | |
@impl true | |
def handle_event("save", _params, socket) do | |
socket = | |
case socket.assigns.form |> AshPhoenix.Form.submit() do | |
{:ok, result} -> | |
socket |> assign(result: result) | |
{:error, form} -> | |
socket |> assign(form: form) | |
end | |
{:noreply, socket} | |
end | |
@impl true | |
def handle_event("add_author", _params, socket) do | |
new_form = socket.assigns.form |> AshPhoenix.Form.add_form(:authors) | |
socket = | |
socket | |
|> assign(form: new_form) | |
{:noreply, socket} | |
end | |
@impl true | |
def handle_event("remove_author", %{"path" => path}, socket) do | |
new_form = socket.assigns.form |> AshPhoenix.Form.remove_form(path) | |
socket = | |
socket | |
|> assign(form: new_form) | |
{:noreply, socket} | |
end | |
end | |
defmodule Router do | |
use Phoenix.Router | |
import Phoenix.LiveView.Router | |
pipeline :browser do | |
plug(:accepts, ["html"]) | |
end | |
scope "/", SamplePhoenix do | |
pipe_through(:browser) | |
live("/", SampleLive, :index) | |
end | |
end | |
defmodule SamplePhoenix.Endpoint do | |
use Phoenix.Endpoint, otp_app: :sample | |
socket("/live", Phoenix.LiveView.Socket) | |
plug(Router) | |
end | |
{:ok, _} = Supervisor.start_link([SamplePhoenix.Endpoint], strategy: :one_for_one) | |
Process.sleep(:infinity) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment