Using Ecto (without Db) for validating Phoenix form

using-ecto-(without-db)-for-validating-phoenix-form

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:

Ecto common case

Actually, we can use only 2 modules are enough for cast & validate 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-Zs]+$/)
    |> 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 cast & validate values from user 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"""
    
bg-red-600 text-white rounded-md">
<.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" >

text-4xl font-bold text-center">New Candidate!


<.input field={f[:name]} placeholder="
Nguyen Van Great" id="name" label="Full Name" />
<.input field={f[:bio]} placeholder="
example: Elixir, Phoenix, Ecto, Hòzô" id="bio" label="Bio"/>
<.button type="
submit">Add Candidate

"""

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 I handle submit from user simple:


  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"https://dev.to/")

      {: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 error in browser.

Now, I have a completed form!

Actually, I have an other tips for user can continue fill to the form in another device/browser or in case if LiveView is crashed.

Check my repo for more.

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
mastering-software-architecture:-the-indispensable-role-of-diagrams

Mastering Software Architecture: The Indispensable Role of Diagrams

Next Post
what-is-process-mapping?

What is Process Mapping?

Related Posts