M岷h V农
Posted on June 8, 2024
I had a sharing session for Elixir community in Saigon about how to using Ecto without db for validating Phoenix form. Now I add this for people who just start to learn Elixir can see what can do with Ecto & Phoenix form.
For save time I just write an example for HTML form in LiveView.
As we known Ecto is very flexible library for Elixir application and people usually use Ecto like:
Actually, we can use only 2 modules are enough for casting & validating form: Ecto Changeset & Schema (you can use only Changeset module but with Schema much more convenience).
For example:
I declare Schema for Candidate module:
defmodule DemoEctoForm.Candidate do
use Ecto.Schema
import Ecto.Changeset
embedded_schema do
field :name, :string
field :bio, :string
end
def changeset(candidate, attrs \\ %{}) do
candidate
|> cast(attrs, [:name, :bio])
|> validate_required([:name])
|> update_change(:name, &String.trim/1)
|> validate_format(:name, ~r/^[a-zA-Z\s]+$/)
|> validate_format(:name, ~r/^\w+(?:\s+\w+){1,5}$/)
|> validate_format(:bio, ~r/^\w+(?:\s+\w+){2,25}$/)
end
end
A embedded schema with only 2 fields & a changeset/2 function for casting & validating values from submitted form.
For validating use can see one required field (:name
) and I add some regex for checking valid fields.
And at LiveView I defined a template:
~H"""
<div class="bg-red-600 text-white rounded-md">
<br>
<.form
id="candidate-form"
:let={f} for={@changeset}
phx-change="validate"
phx-submit="submit"
class="flex flex-col max-w-md mx-auto mt-8"
>
<h1 class="text-4xl font-bold text-center">New Candidate!</h1>
<br>
<.input field={f[:name]} placeholder="Nguyen Van Great" id="name" label="Full Name" />
<br>
<.input field={f[:bio]} placeholder="example: Elixir, Phoenix, Ecto, H貌z么" id="bio" label="Bio"/>
<br>
<.button type="submit">Add Candidate</.button>
<br><br>
</.form>
</div>
"""
I passed default changeset from mount event then use directly in form. See .form
and .input
My mount event:
def mount(_params, _session, socket) do
changeset = Candidate.changeset(%Candidate{})
{:ok, assign(socket, changeset: changeset)}
end
And add phx-submit
event to handle submitted form from user:
def handle_event("submit", %{"candidate" => candidate_params}, socket) do
changeset =
%Candidate{}
|> Candidate.changeset(candidate_params)
if changeset.valid? do
data = apply_action!(changeset, :update)
IO.puts "Candidate data: #{inspect(data, [pretty: true, struct: false])}"
Ets.add_candidate(data)
socket =
socket
|> put_flash(:info, "You added a candidate!")
|> redirect(to: ~p"/")
{:noreply, socket}
else
changeset =
%Candidate{}
|> Candidate.changeset(candidate_params)
{:noreply, assign(socket, changeset: changeset)}
end
end
I cast data from submitted form to changeset then check if changeset is valid or not. If changeset is invalid I pass again socket for form show errors in browser then user can update data.
Now, I have a completed form!
Actually, I have added an other tips for user can continue fill to the form in another device/browser or in case if LiveView is crashed by save state of form to ets
table in my project.
Check my repo for more.
Posted on June 8, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.