creating a form for a non-persisted ecto schema in phoenix 1.4
I kind of wanted to title this post, “how to create a form that doesn’t use a model” but Ecto decided to drop “Model” in favor of “Schema”; same thing, different mindset.
Sometimes you just want to collect some data from a user but have no plans of persisting it to the database. You can use embedded_schema
to accomplish this. You’ll get to mostly work with Phoenix/Ecto as usual except without the migration, tables, and saving.
In this post we’ll be creating a Phoenix 1.4 app with a contact form.
Create the Phoenix 1.4 app
The following command will
- create a new Phoenix app called
Contact
- install its dependencies
- create the database
- open your browser to http://localhost:4000
- start the webserver
A couple things to note:
- You will told the directory already exists and asked if you want to continue: Type
y
and press Enter - You will be asked if you want to “Fetch and install dependencies?”: Type
y
and press Enter - The browser will likely tell you the page isn’t found since it tried to load the site before the webserver finished starting. Simply wait a second or two and then reload the page.
mkdir contact ; cd contact ; mix phx.new . --app contact ; mix ecto.create ; open http://localhost:4000 ; mix phx.server
You should be greeted with the Phoenix welcome screen.
Create the Ecto.Schema
Create a new file \lib\contact\message.ex
with the following contents:
defmodule Contact.Message do
use Ecto.Schema
import Ecto.Changeset
embedded_schema do
field :name
field :email
end
def changeset(message, attrs) do
message
|> cast(attrs, [:name, :email])
|> validate_required([:name, :email])
end
end
Notice we used embedded_schema here. This let’s Ecto know that we don’t plan on saving this schema to the database.
Creating the form
Open \lib\contact_web\templates\page\index.html.eex
and replace with the following:
<div class="section">
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<%= form_for @changeset, Routes.page_path(@conn, :contact), [method: "post"], fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops, you might have missed a field! Please check the errors below.</p>
</div>
<% end %>
<div class="field">
<%= label f, :name %>
<div class="control">
<%= text_input f, :name, [class: "input is-large", placeholder: "Your name"] %>
<%= error_tag f, :name %>
</div>
</div>
<div class="field">
<%= label f, :email, class: "label is-large" %>
<div class="control">
<%= text_input f, :email, [class: "input is-large", type: "email", placeholder: "Your email"] %>
<%= error_tag f, :email %>
</div>
</div>
<div class="control">
<%= submit "Send", class: "button is-primary is-large" %>
</div>
<% end %>
</div>
Hooking up the controller
Open lib\contact_web\controllers\page_controller.ex
and replace with the following:
defmodule ContactWeb.PageController do
use ContactWeb, :controller
alias Contact.Message
def index(conn, _params) do
changeset = Message.changeset(%Message{}, %{})
render(conn, "index.html", changeset: changeset)
end
def contact(conn, %{"message" => message_params}) do
changeset =
%Message{}
|> Message.changeset(message_params)
if changeset.valid? do
# Do something with message_params
conn
|> put_flash(:info, "Message sent!")
|> redirect(to: Routes.page_path(conn, :index))
else
changeset = %{changeset | action: :index}
render(conn, "index.html", changeset: changeset)
end
end
end
- phoenix