iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0
Modern Web

Phoenix 1.7 完全教學系列 第 18

18 修改功能與共用表格

  • 分享至 

  • xImage
  •  

修改連結

這次我們由從 index 的連結開始
notes/index.html.eex 中加上修改的連結,將 <ul> 列表改為:

<ul class="flex flex-col gap-4">
  <li
    :for={note <- @notes}
    class="flex justify-between bg-amber-50 p-4 rounded-lg font-bold border-2 border-amber-300"
  >
    <%= note.content %>
    <a href={~p"/notes/#{note}/edit"} class="text-amber-800">編輯</a>
  </li>
</ul>

在 Router 裡面定義的編輯頁面方式為:

get "/notes/:id/edit", NoteController, :edit

這裡導向編輯的連結使用了 note.id,我們使用 ~p sigil 加上字串插值來產生連結。
(#{note.id} 可省略為 #{note})

https://ithelp.ithome.com.tw/upload/images/20231003/201410541pqHEMb2NM.png

當然,點進去之後還是抱怨了找不到 edit ,我們接著實作。

修改頁面

修改的 edit/update 頁面與新增的 new/create 頁面非常相似,唯一的不同是,新增時我們使用空的 note struct (%Note{}) 來建立 changeset,而修改時我們必須要使用 Notes.get_note 來取得目前的 note struct,並使用它來建立 changeset。

替目前的 NotesController 加上修改的 edit/update 兩個函式

def edit(conn, %{"id" => id}) do
  note = Notes.get_note(id)
  changeset = Note.changeset(note, %{})
  render(conn, :edit, note: note, changeset: changeset)
end

def update(conn, %{"id" => id, "note" => note_params}) do
  note = Notes.get_note(id)

  case Notes.update_note(note, note_params) do
    {:ok, _note} ->
      conn
      |> put_flash(:info, "感激筆記修改成功")
      |> redirect(to: ~p"/notes")

    {:error, %Ecto.Changeset{} = changeset} ->
      render(conn, :edit, note: note, changeset: changeset)
  end
end

lib/gratitude_web/views/note_view.ex 中加上 edit.html.eex 的 view

<header class="my-4 border-b-4 border-amber-600">
  <h1 class="text-xl font-bold text-amber-800">編輯感激筆記</h1>
</header>

<.form :let={form} for={@changeset} action={~p"/notes/#{@note}"}>
  <.input field={form[:content]} />
  <div class="flex justify-between items-center mt-4">
    <a href={~p"/notes"} class="text-amber-800 rounded-lg border-amber-700 p-2">
      返回
    </a>
    <button class="text-amber-800 rounded-lg border-amber-700 px-2 py-1 bg-amber-300">送出</button>
  </div>
</.form>

https://ithelp.ithome.com.tw/upload/images/20231003/20141054W8Ws9Js2RS.png

使用 component 來共用表格

新增與修改的表格長的一模一樣,唯一的差別是 <.form>action 屬性,我們可以將這個表格抽出來成為一個 component,並在新增與修改頁面中共用。

lib/gratitude_web/controllers/note_html.ex 裡新增一個接收 assigns 的 note_form component 函式頭

defmodule GratitudeWeb.NoteHTML do
  use GratitudeWeb, :html

  embed_templates "note_html/*"

  # 新增這一行
  def note_form(assigns)
end

lib/gratitude_web/controllers/note_html/note_form.html.heex 中加上表格的內容,這邊使用 @action 替代掉兩個表格的唯一差異

<.form :let={form} for={@changeset} action={@action}>
  <.input field={form[:content]} />
  <div class="flex justify-between items-center mt-4">
    <a href={~p"/notes"} class="text-amber-800 rounded-lg border-amber-700 p-2">
      返回
    </a>
    <button class="text-amber-800 rounded-lg border-amber-700 px-2 py-1 bg-amber-300">送出</button>
  </div>
</.form>

接著就可以在 new 與 edit 中使用 .note_form component 取代掉原本的表格

New 頁面 (lib/gratitude_web/controllers/note_html/new.html.heex)

<header class="my-4 border-b-4 border-amber-600">
  <h1 class="text-xl font-bold text-amber-800">新增感激筆記</h1>
</header>

<.note_form changeset={@changeset} action={~p"/notes/"} />

Edit 頁面 (lib/gratitude_web/controllers/note_html/edit.html.heex)

<header class="my-4 border-b-4 border-amber-600">
  <h1 class="text-xl font-bold text-amber-800">編輯感激筆記</h1>
</header>

<.note_form changeset={@changeset} action={~p"/notes/#{@note}"} />

重構完成後可以執行測試,確認原本寫的新增有沒有壞掉。

小測驗

參考新增流程的測試寫出修改流程的測試

參考解答

describe "edit note" do
  test "顯示修改筆記表格", %{conn: conn} do
    {:ok, note} = Notes.create_note(%{content: "修改前"})
    conn = get(conn, ~p"/notes/#{note}/edit")
    assert html_response(conn, 200) =~ "編輯感激筆記"
  end
end

describe "update note" do
  test "修改成功後轉址到列表", %{conn: conn} do
    {:ok, note} = Notes.create_note(%{content: "修改前"})
    conn = put(conn, ~p"/notes/#{note}", note: %{content: "修改後"})
    assert redirected_to(conn) == ~p"/notes"

    conn = get(conn, ~p"/notes")
    html = html_response(conn, 200)
    assert html =~ "感激筆記修改成功"
    assert html =~ "修改後"
  end

  test "資料有誤時顯示錯誤", %{conn: conn} do
    {:ok, note} = Notes.create_note(%{content: "修改前"})
    conn = put(conn, ~p"/notes/#{note}", note: %{content: ""})
    html = html_response(conn, 200)
    assert html =~ "編輯感激筆記"
    assert html =~ Plug.HTML.html_escape("can't be blank")
  end
end

上一篇
17 new/create 頁面測試
下一篇
19 刪除與 link component
系列文
Phoenix 1.7 完全教學30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言