Understanding Forms and Changesets
This page is in progress
This tutorial assumes you have a basic understanding of Elixir and that you have explored Phoenix. It also assumes that you understand HTML forms.
The methodology of this tutorial includes converting a conventional HTML form into a Phoenix template that uses Elixir to communicate with back end code. You also learn how changesets integrate with controllers.
The setup for the tutorial requires the creation of a database table named Item and its Ecto "Context" data. We walk through the entire process.
Glossary
- Changeset:
- Phoenix Template:
Getting Started
To begin, create a new empty Phoenix app named app. If you do not know how to do that follow this tutorial:
How to Create an Empty Phoenix Application
When complete, run this command to create database tables and Ecto context code.
mix phx.gen.context Items Item items name:string
Run the migrate command
mix ecto.migrate
Seed Data
In the file named app/priv/repo/seeds.ex type the following code to create "dummy data" for this exercise.
App.Items.create_item(%{name: "item-1"}) App.Items.create_item(%{name: "item-2"}) App.Items.create_item(%{name: "item-3"})
In the terminal type:
mix run priv/repo/seeds.exs
Routes
In routes, add the the following routes.
get "/items", ItemController, :index
Controller
In app/lib/app_web/controllers create a file controller named:
item_controller.ex
Copy the following code into it.
defmodule AppWeb.ItemController do use AppWeb, :controller def index(conn, _params) do # The home page is often custom made, # so skip the default app layout. render(conn, :index, layout: false) end end
In the same directory create a file named:
item.html.ex
Open the file and type the following code:
defmodule AppWeb.ItemHTML do use AppWeb, :html embed_templates "item_html/*" end
Create a folder named index_html in the same directory like this:
app/lib/app_web/controllers/index_html
In index_html create a file named index.html.heex and copy type the following code into it.
<div> Items go here</div>
Start the app and make sure everything works.
mix phx.server
In your browser go to:
http://localhost:4000/items
You will see the phrase "Items go here" in the upper left corner of the screen.
Capture Parameter Data of URL Link
When a user clicks a hyper-link you should know how to capture the url parameters via controller. To do so follow these steps:
Open the router file router.ex. Create a new route that looks like this:
get "/items/:item", ItemController, :index
scope "/", AppWeb do get "/items/:data", ItemController, :index #:data is a variable get "/items", ItemController, :index get "/", PageController, :home end
The :item is a variable and represents a value that is unknown. When you type a URL like
localhost:4000/item/some-data
The value "some-data" is assigned to the variable :data as a string and the variable is captured in the controller. Once in the controller, the data can be manipulated.
Open the item_controller.ex
Place IO.inspect(_params) in the Item index controller body like this:
def index(conn, _params) do IO.inspect _params render(conn, :index, layout: false) end
Open the Item template in app_web/controllers/item_html/index.html.heex
Write a hyper link like this:
<a href ="items/some-data-goes-here>Click me</a>
Start the server, go to localhost:4000/items/some-data-goes-here
Open the terminal. While it is open click the link in the website that says Click me.
In the terminal the the following text is presented:
%{"data" => "some-data-goes-here"}
The key is the name of the variable you placed in the route (data) and the string is the data assigned to it in the hyper link (some-data-goes-here)
Submitting Data Through an HTML Form to a Controller
You are now going to write code that lets you submit data through a form and inspect that data from within a controller.
Elixir Phoenix has it's own syntax for writing forms. This syntax is named Embedded Elixir or Heex for short. It lets you write Elixir code in your HTML template files to work with data from the server, database and user interface.
An example of a form with Heex used looks like this:
<%= form_for @changeset, Routes.user_path(@conn, :create), fn f -> %> <%= text_input f, :name %> <%= inputs_for f, :permalink, fn fp -> %> <%= text_input fp, :url %> <% end %> <% end %>
Prior to using Heex, you will create a working HTML form without Heex, and gradually integrate Heex into it.
To do this you need to create:
- A POST route
- A Controller for the POST
- An Html form
HTML Form in Template
Go to your item index template: app_web/controllers/item_html/index.html.heex
Create basic HTML form.
<form method="POST" action="/submit"> <input type="text" name="example" /> <input type="submit"> </form>
Route
In your router, create the following route. The endpoint must be "submit".
post "/submit", ItemController, :index
Controller
def submit(conn, _params) do # The home page is often custom made, # so skip the default app layout. IO.inspect _params redirect(conn, to: "/items") end
Go to localhost:4000/items
You will see a form. Click the submit button.
An error will appear:
invalid CSRF (Cross Site Request Forgery) token, please make sure that: * The session cookie is being sent and session is loaded * The request include a valid '_csrf_token' param or 'x-csrf-token' header
To fix the error add the following line to your form right above the index element.
<%# <input type="hidden" name="_csrf_token" value={@csrf_token} /> %>
It will look like this:
<form method="POST" action="/submit"> <input type="hidden" name="_csrf_token" value={@csrf_token} /> <input type="text" name="example" /> <input type="submit"> </form>
The controller now needs to be configured to render the CSRF token. Change your Item controller code to reflect the following example:
def index(conn, _params) do IO.inspect _params csrf_token = Plug.CSRFProtection.get_csrf_token() render(conn, :index, [csrf_token: csrf_token]) end
Start the server and go to localhost:4000/items
Submit data to the form.
In the terminal, the submitted data and CSRF content is viewable.
%{ "_csrf_token" => "CAgbPih1d3QuUwIpW1t1byQvEAIUQz8beoJkRF4BxcNv38LYLHr6ZnLo", "example" => "Test" }
The key "example" is the name of the form, and has your form submission data.
This page is in progress