Phoenix for Railsies: Form Helpers and Models

Share this article

phoenix

Phoenix is a web framework built for Elixir, an awesome language that’s built on the Erlang Virtual Machine. Coming from a Rails background, Phoenix seems immediately familiar. Although it is missing a few of the niceties that Rails gives us, Phoenix has excellent support for technologies such as WebSockets, which makes it a great candidate for modern web development. Secondly, since it is built on the Erlang Virtual Machine, many benchmarks show that it can scale better than Rails. In any case, it’s a great tool to add to any Rubyist’s belt.

We covered some of the basics of Phoenix in my last article. This time around, we’ll build out the Remynders app with form helpers and models. Let’s jump right in. You can find the Github repository for the code covered in this article and the last one here.

Form HTML

If you dial back to where we left off last time around (check the “article1” branch of the repository), we set up the index view, but wanted to set up a form to create a new reminder. This requires the creation an empty view in web/views/reminder_view.ex:

defmodule Remynders.ReminderView do
  use Remynders.Web, :view
end

We still need a template to actually show the user the form. Let’s do that by creating the template in templates/reminder/new.html.eex:

<form>
  <div class="input_wrapper center_block">
    <label>Reminder text</label>
    <input type="number" value="10"></input>
  </div>

  <div class="input_wrapper center_block">
    <label>Minutes from now</label>
    <input type="number" value="10"></input>
  </div>

  <div class="input_wrapper center_block">
    <label>Your email</label>
    <input type="email"></input>
  </div>

  <div class="input_wrapper center_block">
    <input type="submit" value="Remynd me" class="button"></div>
  </div>
</form>

BOO to ugly, unstyled input fields, so add some CSS in web/static/app.scss:

textarea:focus, input:focus{
    outline: 0;
}

label {
  font-weight: bold;
  font-size: 20px;
  display: block;
  margin: 20px;
}

.input_wrapper {
  margin-top: 20px;
}

input[type="number"], input[type="email"], input[type="number"]:active {
  font-size: 25px;
  text-align: center;
  font-weight: bold;
  border: 5px solid #000;
  padding: 15px;
  width: 225px;
}

input[type="email"] {
  font-size: 18px;
}

input[type="submit"] {
  width: 265px;
  padding: 15px;
  border-color:; #cc0000;
  background-color: #fff;
}

Now, taking a look at http://127.0.0.1:4000/reminders/new, there’s the form. But, wait a second, we’re not actually populating a model with this form. To do that, use the form_for form helper. Before we do that, it’s time set up our models.

Models

We want a model to represent the concept of a reminder. In Phoenix, Ecto is used to do database-related stuff. Basically, it’s a way to write queries in Elixir through a domain specific language. This is a bit different from the OOP-based Rails approach, but most of the setup is very similar.

In Rails, we’d shoot off a rails generate model command followed by a rake db:migrate. Phoenix is pretty similar:

mix phoenix.gen.model Reminder reminders minutes:integer email:string title:string

We should get output like the following:

  • creating priv/repo/migrations/20150805211918_create_reminder.exs
  • creating web/models/reminder.ex
  • creating test/models/reminder_test.exs

There, the model is created. But, we need to make space for it inside our database. Ecto comes with adapters for MySQL and PostgreSQL. By default, Phoenix sets you up with the PostgreSQL adapter. To get it going, we need a couple of commands (edit config/dev.exs as needed for your PostgreSQL setup):

$ mix ecto.create
$ mix ecto.migrate

Phoenix borrows a lot of ideas from Rails and Rails-like frameworks, including migrations. In priv/repo/migrations, you’ll find the migration for the model and database table set up by those two commands.

Form Helpers

Now that we have the model, let’s set up the form to actually use the model. Take a look at the model in web/models/reminder.ex:

defmodule Remynders.Reminder do
  use Remynders.Web, :model

  schema "reminders" do
    field :minutes, :integer
    field :email, :string
    field :title, :string

    timestamps
  end

  @required_fields ~w(minutes email title)
  @optional_fields ~w()

  @doc """
  Creates a changeset based on the `model` and `params`.

  If `params` are nil, an invalid changeset is returned
  with no validation performed.
  """
  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
  end
end

The important method here is changeset. We needn’t worry ourselves too much about how it works at this point, but calling the method basically creates a “changeset” for the model, in case we want to update it. We need to update our controller code (web/controllers/reminder_controller.ex) to allow the template to access the changeset for a new Reminder instance:

def new(conn, _params) do
  changeset = Reymnders.Reminder.changeset(%Reymnders.Reminder{})
  render conn, "new.html", changeset: changeset
end

What is going on here? Basically, we’re setting up a changeset from a new instance of Reminder and passing that to the template. Let’s change the template (web/templates/reminder/new.html.eex) to:

<%= form_for @changeset, reminder_path(@conn, :create), fn f -> %>
  <div class="input_wrapper center_block">
    <label>Reminder text</label>
    <%= text_input f, :title %>
  </div>

  <div class="input_wrapper center_block">
    <label>Minutes from now</label>
    <%= number_input f, :minutes, [{:value, "10"}] %>
  </div>

  <div class="input_wrapper center_block">
    <label>Your email</label>
    <%= text_input f, :email %>
  </div>

  <div class="input_wrapper center_block">
    <%= submit "Remynd me" %>
  </div>
<% end %>

As you can see, the HTML is now using form helpers. Now, we need to add a create action to our controller:

def create(conn, %{"reminder" => reminder_params}) do
  changeset = Reminder.changeset(%Reminder{}, reminder_params)
  case Repo.insert(changeset) do
    {:ok, _reminder} ->
      conn
      |> put_flash(:info, "Reminder created successfully.")
      |> redirect(to: "/")
    {:error, changeset} ->
      render(conn, "new.html", changeset: changeset)
  end
end

Also, in order to be able to refer to the Reminder model as just Reminder rather than Remynders.Reminder, create an alias just as we need for Helpers the last time around. So, stick this just after the defmodule line in the controller:

alias Remynders.Reminder

Checking quickly with a filled out form, it is evident that our code works, but it’s pretty opaque at the moment. Let’s break it down bit by bit.

def create(conn, %{"reminder" => reminder_params}) do

This looks like a pretty innocuous function definition until we look at the second parameter. What the heck is going on?! This is using one of Elixir’s functional-programming-like features: pattern matching. We’re saying that out of the second argument to create, expect to get a dictionary. We want the value associated with the key “reminder” within this dictionary to be placed into the variable reminder_params so it can referenced later in the method.

changeset = Reminder.changeset(%Reminder{}, reminder_params)

This is constructing a changeset out of the reminder parameters we’ve just been handed. Specifically, it’s an empty Reminder instance with %Reminder{} and a set of values that it’s supposed to have with reminder_params. Then, Reminder.changeset figures out what is needed to change in order to get from the first argument to have the values in the second argument.

case Repo.insert(changeset) do

The first important part to consider is Repo.insert(changeset). Generally, database changes are performed through the Repo module. With the statement, we’re telling Ecto to insert the changeset into the underlying database. Next up, consider the case keyword. It’s a bit like a switch statement (if you have a C/Java background) on the return value of Repo.insert but about a million times more powerful. Essentially, we are pattern matching against the return value.

{:ok, _reminder} ->
    conn
    |> put_flash(:info, "Reminder created successfully.")
    |> redirect(to: "/")

This is a part of the case statement. Here, we’re considering the case where the Reminder is successfully inserted into the database. Specifically, the code in this block should execute if we get a given format of return value (i.e. with an :ok instead of an :error). In order to understand the rest of the code, we need to understand the |> operator. Basically, a |> b(c, d) is a different way of writing b(a, c, d), i.e. the left hand is evaluated then passed as the first argument to the right half. If by some wild chance you’re into Haskell, it’s a bit like the reverse of the $ operator. If by some even wilder chance you’re into F#, then it’s like the pipe operator, more or less.

We are chaining together these method calls using the |> operator because both put_flash and redirect_to require conn as their first argument. This puts in a “flash” notice (just as we would with flash[:notice] in Rails) and then redirects to the root of the application.

{:error, changeset} ->
    render(conn, "new.html", changeset: changeset)

The second case is much simpler. In case something goes wrong, just pass the changeset back to the page we were just on in order to load the form with the values the user has already supplied. But, how do we tell the user what exactly went wrong?

Wrapping It Up

That, and a number of other things will be topics for the next post. We still have to implement notices and errors and a way to deliver Remynders to the user on a timely basis. We’ll be covering both of those things as well as some other tidbits in the next installment. In writing this article, I’ve realized that Phoenix currently doesn’t have amazing, up-to-date documentation, which is a bit much to expect of a project that’s still up and coming. Hopefully this guide helps fill that void and provides a quick on-boarding process for Railies looking to try out something new.

Frequently Asked Questions (FAQs) about Phoenix for Railsies: Form Helpers and Models

How does Phoenix compare to Rails in terms of form helpers and models?

Phoenix and Rails both offer robust form helpers and models, but they differ in their implementation. Phoenix uses Ecto for handling forms and models, which is a database wrapper and query generator. It provides a DSL for writing queries and interacting with databases. On the other hand, Rails uses ActiveRecord, an Object-Relational Mapping (ORM) system. While both are powerful, Ecto offers more flexibility and control, which can be beneficial in complex applications.

What are the key differences between form_for, form, and f.form_for in Phoenix?

These are different ways to handle forms in Phoenix. ‘form_for’ is a function that generates a form for a given data structure. ‘form’ is a more general function that can be used to create a form without a specific data structure. ‘f.form_for’ is a function that is used within a form_for block to generate form fields. Each has its own use cases and can be used depending on the specific requirements of your application.

How do I create a form using Phoenix LiveView?

Creating a form in Phoenix LiveView involves several steps. First, you need to define the form in your LiveView module, using the ‘form_for’ function. Then, you need to handle the form submission in the ‘handle_event’ function. This involves validating the form data and performing any necessary actions, such as saving the data to a database. Finally, you need to render the form in your LiveView template, using the ‘f’ function to generate the form fields.

What is the purpose of Phoenix.HTML.Form?

Phoenix.HTML.Form is a module in Phoenix that provides functions for generating HTML forms. It includes functions for creating form tags, input fields, text areas, select boxes, checkboxes, radio buttons, and more. It also provides functions for handling form submissions and displaying form errors.

How do I use form bindings in Phoenix LiveView?

Form bindings in Phoenix LiveView allow you to bind form inputs to the state of your LiveView. This means that when a user interacts with a form input, the state of your LiveView is automatically updated. To use form bindings, you need to use the ‘phx-change’ attribute in your form inputs. This attribute specifies the event that should be triggered when the form input changes.

How do I handle form errors in Phoenix?

Handling form errors in Phoenix involves using the ‘Ecto.Changeset’ module. This module provides functions for validating form data and generating error messages. When a form is submitted, you can use the ‘cast’ function to validate the form data and the ‘validate_required’ function to ensure that all required fields are filled in. If there are any errors, they can be displayed using the ‘error_tag’ function.

How do I use the ‘form_for’ function in Phoenix?

The ‘form_for’ function in Phoenix is used to generate a form for a given data structure. It takes three arguments: the data structure, the action to be performed when the form is submitted, and a function that generates the form fields. The function that generates the form fields is typically a ‘do’ block that uses the ‘f’ function to create the form fields.

How do I use the ‘f’ function in Phoenix?

The ‘f’ function in Phoenix is used within a ‘form_for’ block to generate form fields. It takes two arguments: the name of the field and a set of options. The options can include things like the type of input (e.g., text, password, checkbox), the value of the input, and any HTML attributes.

How do I validate form data in Phoenix?

Validating form data in Phoenix involves using the ‘Ecto.Changeset’ module. This module provides functions for validating form data, such as ‘cast’ (which checks that the form data is of the correct type) and ‘validate_required’ (which checks that all required fields are filled in). If the form data is not valid, these functions will return a changeset with errors that can be displayed to the user.

How do I handle form submissions in Phoenix?

Handling form submissions in Phoenix involves using the ‘handle_params’ function in your controller. This function is called when a form is submitted and is responsible for validating the form data and performing any necessary actions, such as saving the data to a database. If the form data is valid, the ‘handle_params’ function should redirect the user to a new page. If the form data is not valid, the function should re-render the form with the errors.

Dhaivat PandyaDhaivat Pandya
View Author

I'm a developer, math enthusiast and student.

elixirGlennG
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week