Lesson 7

Adding Edit and Update Functionality

Most CRUD applications include forms to edit an existing record in the database, and react to the update button being pressed. In this lesson we will setup this functionality in our web application.

Edit Page

The index page should include a link to edit the record, so edit web/templates/quote/index.eex to look like this:

<table>
  <thead>
    <tr>
      <th>Saying</th>
      <th>Author</th>
<th>Actions</th>
</thead> <%= for q <- @quotes do %> <tr> <td> <a href="<%=quote_path(@conn, :show, q.id) %>"> <%= q.saying %> </a> </td> <td> <%= q.author %> </td>
<td> <a href="<%=quote_path(@conn, :edit, q.id) %>"> Edit </a> </td>
</tr> <% end %> </table>

Save the file.

Navigate to localhost:4000/quotes, notice the edit link we added was added to the page. When you press the link it will present you with an error message that indicates that the action isn't present in the controller. Let's add it!

Edit web/controllers/quote_controller.ex to look like this:

defmodule Splurty.QuoteController do
  use Phoenix.Controller

  alias Splurty.Router
  import Splurty.Router.Helpers

  plug :action

  def homepage(conn, _params) do
    render conn, "homepage.html"
  end

  def index(conn, _params) do
    conn
    |> assign(:quotes, Repo.all(Splurty.Quote))
    |> render("index.html")
  end

  def new(conn, _params) do
    render conn, "new.html"
  end

  def create(conn, %{"quote" => %{"saying" => saying, "author" => author}}) do
    q = %Splurty.Quote{saying: saying,  author: author}
    Repo.insert(q)

    redirect conn, to: quote_path(conn, :index)
  end

  def show(conn, %{"id" => id}) do
    {id, _} = Integer.parse(id)
    conn
    |> assign(:quote, Repo.get(Splurty.Quote, id))
    |> render("show.html")
  end
  
def edit(conn, %{"id" => id}) do {id, _} = Integer.parse(id) conn |> assign(:quote, Repo.get(Splurty.Quote, id)) |> render("edit.html") end
end

Save the file.

This action is doing the same thing as the show action, it's loading up the quote and storing it as the assignment @quote we can use within the template.

Refresh the edit page, and you will see an error that the template doesn't exist yet. Let's add one now!

Add a file in web/templates/quote called edit.html.eex and populate it with this content:

Hello!

Save the file and refresh the page. The error message vanished, and we now are present with: Hello!

Replace the content of the page with a form, similar to new.html.eex, but with some tweaks. Adjust web/templates/quote/edit.html.eex to look like this:

<h1>Edit Quote</h1>
<form action="<%= quote_path(@conn, :update, @quote.id) %>" method="post">
  <div class="form-group">
    <input type="hidden" name="csrf_token" value="<%= csrf_token(@conn) %>">
    <input type="hidden" name="_method" value="put">

    <label for="quote[saying]">saying</label>
    <input type="text" name="quote[saying]" class="form-control" value="<%= @quote.saying %>" />

    <label for="quote[author]">author</label>
    <input type="text" name="quote[author]" class="form-control" value="<%= @quote.author %>" />

  </div>
  <button type="submit" class="btn btn-primary">Save</button>
</form>

Save the file and refresh the page. You will be presented with a pre-populated form for the item we are editing.

Update Action

On the edit page, adjust the values in the form and press the submit button. You will be presented an error message that there is an undefined controller action for update. Let's add one!

Edit web/controllers/quote_controller.ex to support update functionality:

defmodule Splurty.QuoteController do
  use Phoenix.Controller

  alias Splurty.Router
  import Splurty.Router.Helpers

  plug :action

  def homepage(conn, _params) do
    render conn, "homepage.html"
  end

  def index(conn, _params) do
    conn
    |> assign(:quotes, Repo.all(Splurty.Quote))
    |> render("index.html")
  end

  def new(conn, _params) do
    render conn, "new.html"
  end

  def create(conn, %{"quote" => %{"saying" => saying, "author" => author}}) do
    q = %Splurty.Quote{saying: saying,  author: author}
    Repo.insert(q)

    redirect conn, to: quote_path(conn, :index)
  end

  def show(conn, %{"id" => id}) do
    {id, _} = Integer.parse(id)
    conn
    |> assign(:quote, Repo.get(Splurty.Quote, id))
    |> render("show.html")
  end

  def edit(conn, %{"id" => id}) do
    {id, _} = Integer.parse(id)
    conn
    |> assign(:quote, Repo.get(Splurty.Quote, id))
    |> render("edit.html")
  end


def update(conn, %{"id" => id, "quote" => %{"saying" => saying, "author" => author}}) do {id, _} = Integer.parse(id) q = Repo.get(Splurty.Quote, id) q = %{q | saying: saying, author: author } Repo.update(q) redirect conn, to: quote_path(conn, :show, q.id) end
end

Save the file.

This code loads the proper quote and performs an update, just like we did in Lesson 3 from within iex. It then redirects the user to the detail page for the quote they edited.

Try to update the quote again from the form. Awesome, it works! :)

Your application should look just like this at this step.

Next Lesson