Skip to content

Instantly share code, notes, and snippets.

@nallwhy
Last active June 26, 2025 23:15
Show Gist options
  • Save nallwhy/019255c704628216c4679b0023071db6 to your computer and use it in GitHub Desktop.
Save nallwhy/019255c704628216c4679b0023071db6 to your computer and use it in GitHub Desktop.
Application.put_env(:sample, SamplePhoenix.Endpoint,
adapter: Bandit.PhoenixAdapter,
http: [ip: {127, 0, 0, 1}, port: 5001],
server: true,
live_view: [signing_salt: "aaaaaaaa"],
secret_key_base: String.duplicate("a", 64)
)
Mix.install([
{:bandit, "~> 1.7"},
{:phoenix_live_view, "~> 1.0"},
{:ash, "3.5.23"},
{:ash_phoenix, "2.3.7"}
])
defmodule LineItem do
use Ash.Resource, data_layer: :embedded
attributes do
attribute :name, :string, allow_nil?: false
attribute :taxable, :boolean, allow_nil?: false, default: true, public?: true
end
actions do
create :create do
primary? true
accept [:name, :taxable]
change fn cs, ctx ->
# always true
cs |> Ash.Changeset.get_attribute(:taxable) |> IO.inspect()
cs
end
end
update :update do
primary? true
accept [:name, :taxable]
end
end
end
defmodule Estimate do
use Ash.Resource, data_layer: Ash.DataLayer.Ets, domain: Domain
attributes do
uuid_primary_key :id
attribute :issue_date, :date, allow_nil?: false
attribute :discountable, :boolean, default: false
attribute :line_items, {:array, LineItem} do
allow_nil? false
default []
constraints [min_length: 1]
public? true
end
end
actions do
create :create do
primary? true
accept [:issue_date, :discountable, :line_items]
end
update :update do
primary? true
accept [:issue_date, :discountable, :line_items]
end
end
end
defmodule Domain do
use Ash.Domain
resources do
resource Estimate
end
end
defmodule SamplePhoenix.ErrorView do
def render(template, _), do: Phoenix.Controller.status_message_from_template(template)
end
defmodule SamplePhoenix.SampleLive do
use Phoenix.LiveView, layout: {__MODULE__, :live}
def mount(_params, _session, socket) do
form =
Estimate
|> AshPhoenix.Form.for_create(:create, forms: [auto?: true])
|> to_form()
|> AshPhoenix.Form.add_form([:line_items, 0])
socket =
socket
|> assign(form: form)
|> assign(result: nil)
{:ok, socket}
end
# layout
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
def render(assigns) do
~H"""
<.form id="form" for={@form} phx-change="validate" phx-submit="save">
<div>
<label for={@form[:issue_date].name}>issue_date</label>
<input
type="date"
id={@form[:issue_date].id}
name={@form[:issue_date].name}
value={@form[:issue_date].value}
/>
</div>
<div>
<label for={@form[:discountable].name}>discountable</label>
{@form[:discountable].value}
<input
type="checkbox"
id={@form[:discountable].id}
name={@form[:discountable].name}
checked={@form[:discountable].value}
/>
</div>
<.inputs_for :let={fl} field={@form[:line_items]}>
<div>
<label for={fl[:name].name}>name</label>
<input type="text" id={fl[:name].id} name={fl[:name].name} value={fl[:name].value} />
</div>
<div>
<label for={fl[:taxable].name}>taxable</label>
{fl[:taxable].value}
<input type="checkbox" id={fl[:taxable].id} name={fl[:taxable].name} checked={fl[:taxable].value} />
</div>
</.inputs_for>
<button type="submit" disabled={[email protected]?}>Save</button>
</.form>
<!--
<div :if={@result}>
result: <%= inspect(@result) %>
</div>
form: <%= inspect(@form) %>
-->
"""
end
def handle_event("validate", %{"form" => params}, socket) do
form = socket.assigns.form |> AshPhoenix.Form.validate(params)
socket =
socket
|> assign(form: form)
{:noreply, socket}
end
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
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