Setting up a new Phoenix 1.5 project! (With Phoenix LiveView!)
Tags: elixir, phoenix, tutorials
Published: 2020-04-25 22:10:42

---
title: "Setting up a new Phoenix 1.5 project! (With Phoenix LiveView!)"
published: true
tags: [elixir, phoenix, tutorials]
---

Phoenix 1.5 is out! Phoenix Live view is now part of the Phoenix Framework! For a quick introduction to what Phoenix Live View is about, checkout [this video](https://www.youtube.com/watch?v=MZvmYaFkNJI&feature=emb_title) by Chris McCord, the creator of Phoenix. This series will dive a bit more into setting up a new Phoenix app.

Check out the [source repo](https://github.com/joseph-lozano/Feenix)

- Part 1 will be about generating scaffolding code, and explaining a bit about how live view works.
- In [part 2](https://dev.to/joseph_lozano/magic-sign-in-links-with-elixir-and-phoenix-1-5-496k) we will implement magic links, ala slack, and we will hide the app behind the login.

As in the video, we initialize the project with `--live`

```bash
mix archive.install hex phx_new 1.5.1 # Install the 1.5.1 phx_new generator
mix phx.new feenix --live && cd feenix # Create and enter the project
git init && git add -A && git commit -m "init" # create init commit
```

#### Users Users Users

Users are the center piece of any application. Our application will make use of ~Magic Links~ ala Slack, so no need for a password field. Let's generate our Accounts context and User schema.

```bash
mix phx.gen.live Accounts User users email:string username:string
```

Let's look at this command piece by piece

1. `mix phx.gen.live` New live view generators in Phoenix 1.5. The live view equivalent of `mix phx.gen.html`
2. `Accounts` The name of our domain context
3. `User` The module name for the schema we are creating
4. `users` The name of the database field attached to the schema
5. `email:sttring username:string` The fields and their types

Add the live routes to your browser scope in `lib/feenix_web/router.ex`:

```elixir
live "/users", UserLive.Index, :index
live "/users/new", UserLive.Index, :new
live "/users/:id/edit", UserLive.Index, :edit

live "/users/:id", UserLive.Show, :show
live "/users/:id/show/edit", UserLive.Show, :edit
```

Next, let's go into the `CreateUsers` migration and unique indexes for email and username to the users table. The changeset function should now look like

```elixir
# priv/repo/migrations/XXXXXXXXXXX_create_users.exs
def change do
  create table(:users) do
    add :email, :string
    add :username, :string

    timestamps()
  end

  create unique_index(:users, :email)
  create unique_index(:users, :username)
end
```

This will both create an index in the database (for fast searching) and create a unique constraint.

Next, let's update the changeset to enforce the new unique constraint. The new changeset should look like

```elixir
# lib/feenix/acconts/user.ex
def changeset(user, attrs) do
  user
  |> cast(attrs, [:email, :username])
  |> validate_required([:email, :username])
  |> unique_constraint(:email,
    name: "users_email_index",
    message: "Account already exists. Please log in."
  )
  |> unique_constraint(:username,
    name: "users_username_index",
    message: "Username already in use. Please use another."
  )
end
```

And lastly, let's just fix the input in our new/edit form

```elixir
# lib/feenix_web/live/user_live/form_component.html.leex
<%= email_input f, :email %> # change text_input to email_input
```

Now that we have the users set up, let's commit

```bash
git add -A && git commit -m "Set up user accounts"
```

### Looking around

Let's check out our new application. and compare it to what we would have gotten if we used `phx.gen.html` as our scaffolding generator.

First thing to notice is that there are no controllers. Instead there is a `live/user_live` folder which contains both the LiveViews and templates. Also of note, is that there are only 2 LiveViews, `Index` and `Show` (as well as a `FormComponent`). Let's start with the index template.

#### Live Templates

```elixir
# lib/feenix_web/live/user_live/index.html.leex
<h1>Listing Users</h1>

<%= if @live_action in [:new, :edit] do %>
  <%= live_modal @socket, FeenixWeb.UserLive.FormComponent,
    id: @user.id || :new,
    title: @page_title,
    action: @live_action,
    user: @user,
    return_to: Routes.user_index_path(@socket, :index) %>
<% end %>

<table>
# ommited for space
  <span><%= live_redirect "Show", to: Routes.user_show_path(@socket, :show, user) %></span>
  <span><%= live_patch "Edit", to: Routes.user_index_path(@socket, :edit, user) %></span>
  <span><%= link "Delete", to: "#", phx_click: "delete", phx_value_id: user.id, data: [confirm: "Are you sure?"] %></span>
# ommited for space
</table>

<span><%= live_patch "New User", to: Routes.user_index_path(@socket, :new) %></span>
```

The first interesting thing on the page is the `if @live_action` check, within which is a function `live_modal`. After that, the familar table. Each row in the table has a link to "Show" "Edit" and "Delete". But take a look at them a bit more closely. The "Edit" link is not a link at all. It is `live_patch`. And the href goes to the `user_index_path` with the `:edit` action. This is the `@live_action` above. So clicking on that link will trigger the modal, since it will set `@live_actiton` to `:edit`

Next the "Delete" link navigates to `"#"`. It has an attribute `phx_click: "delete"`. We will see how this works once we check out the live view.

Finally, the "New User" link at the bottom is also a `live_patch` like "Edit" above, however this time the action is `:new`.

Next, let's look at the Index LiveView proper

#### LiveViews

```elixir
# lib/feenix_web/live/user_live/index.ex
defmodule FeenixWeb.UserLive.Index do
  use FeenixWeb, :live_view

  @impl true
  def mount(_params, _session, socket) do
    {:ok, assign(socket, :users, fetch_users())}
  end

  @impl true
  def handle_params(params, _url, socket) do
    {:noreply, apply_action(socket, socket.assigns.live_action, params)}
  end

  defp apply_action(socket, :edit, %{"id" => id}) do
    socket
    |> assign(:page_title, "Edit User")
    |> assign(:user, Accounts.get_user!(id))
  end

  def handle_event("delete", %{"id" => id}, socket) do
    user = Accounts.get_user!(id)
    {:ok, _} = Accounts.delete_user(user)

    {:noreply, assign(socket, :users, fetch_users())}
  end
  # rest ommited for space
end
```

The `mount` function sets up page initially. In this case, you can see that the socket is being assigned `:users` with users fetched from the database

`handle_params` is triggered by clicking on links with the `live_patch` function. The `@live_action` of `:edit`, `:new`, or `:index` is set automatically, and the appropriate assigns are applied to the socket.

Finally, `handle_event` is what was triggered with the `phx_click` attribute. The attribute value is the first arguement to `handle_event`.

The "show" template and live view is largely similar.

#### Live Components

Components are a mechanism to compartmentalize state, markup, and events in LiveView.

To see how this is being used in our app, first let's check out our live helpers at `lib/feenix_web/live/live_helpers.ex`

```
# lib/feenix_web/live/live_helpers.ex
def live_modal(socket, component, opts) do
  path = Keyword.fetch!(opts, :return_to)
  modal_opts = [id: :modal, return_to: path, component: component, opts: opts]
  live_component(socket, FeenixWeb.ModalComponent, modal_opts)
end
```

This explains the `live_modal` functions in our templates from earlier. We see here that `live_modal` is just a helper to invoke `live_component` for our ModalComponent.

Check out the ModalComponent, we see some an inlined live view template, which itself calls `live_component` with `@component`. `@component` here is our FormCoponent.

Components compartmentalize state, and we see for our `FormComponent`, it looks like a live view all on its own. There is a key difference though, since the unlike the LiveView, a Component, is not a separate proess (it is part of its parent LiveView process), it does not have a `handle_info` callback. Instead, from the parent LiveView, we can use `send_update` to update the Component state.

The `update` callback on Component is also invoked after `mount`.

### Closing thoughts

One of my favorite things about Elixir and Phoenix is that there isn't a lot of magic that happens. And the magic that does happen is at just the right point of abstraction. As a developer, you define templates which get populated but the socket assigns. If the assigns change, the rendered page changes. It's really that simple.

If you have any corrections to this post, or anything that you feel like should have been included, please feel free to reach out to me at [joseph@joseph-lozano.com](mailto:joseph@joseph-lozano.com)

### Edit:

So, by adding our unique contraints, we actually broke a few of the tests that came with our generator. Let's fix them.

The root of the issue is that we are creating a fixture user as part of the setup. Let's just create another set of attributes `@save_attrs %{email: "other@email.com", username: "some username"}`

Then, update the failing test to use the new attrs.
```elixir
# test/feenix_web/live/user_live_test.exs:45
{:ok, _, html} =
  index_live
  |> form("#user-form", user: @save_attrs)
  |> render_submit()
  |> follow_redirect(conn, Routes.user_index_path(conn, :index))
```

In future posts we will make use of TDD to avoid this issue in the future.


Originally posted at https://joseph-lozano.com