我們如果要新增一個 note 會有兩個步驟,第一個是使用著跟我們的伺服器要求可以填寫內容的頁面(get),再來是送出表格(post)。在我們的 router 章節中,我們已經設定了相對應的 new 與 create 路徑:
get "/notes/new", NoteController, :new
post "/notes", NoteController, :create
先從 new 開始
get new 頁面與 index 唯一的差別是,我們提供在畫面的是準備填寫的表格,而不是所有的 note。其他的機制都相同,所以在 router 呼叫 NoteController 的 new 函式時,函式內要做的事情如下:
在 NoteController 中加入 new 函式:
def new(conn, _params) do
changeset = Note.changeset(%Note{}, %{})
render(conn, :new, changeset: changeset)
end
記得在 module 上面加上 alias Gratitude.Notes.Note,這樣我們就可以直接使用 Note 來呼叫該 module 的函式。
Note.changeset/2
第一個是改變集準備要修改的目標,因為我們這次是要準備新增,所以可以給他一個空的 Note struce,第二個參數是我們要修改的內容,因為這一個 changeset 是要給使用者填寫的,我們可以給空的 map 即可,未來如果想要加上預設值,或是隱藏欄位需要有起始值都可以在這邊加入。
這時候使用瀏覽器開啟 http://localhost:4000/notes/new
會看到我們預期的缺少 template 錯誤:
與 index 的頁面一樣,在 lib/gratitude_web/controllers/note_html
底下新增一個 new.html.eex
,並且加入以下內容:
<header class="my-4 border-b-4 border-amber-600">
<h1 class="text-xl font-bold text-amber-800">新增感激筆記</h1>
</header>
現在可以看到 http://localhost:4000/notes/new
頁面可以成功的顯示了,現在我們只剩下表格的部分了。
儘管我們可以使用單純的 html 表格
<form action="/notes" method="post">
<input type="text" />
</form>
但是這樣的話,我們就需要自己處理所有的事情,從安全性的 csrf 跨站請求偽造防護,到有一致的格式取得表格內容等。
Phoenix 提供了 <.form>
component
在 header 下方輸入下面內容
<.form :let={form} for={@changeset} action={~p"/notes"}>
<.input field={form[:content]} />
<button>送出</button>
</.form>
.form
函式幫我們做了很多事,依據在我們目前的 Controller-View 或是在之後會介紹的 LiveView,會有不同的行為寫法,在這個時候我們先知道在目前的使用情境中,我們提供 changeset 到 .form
的 for
attribute,他會幫我們把 changeset 轉成這個表格欄位會用到的表格格式,我們可以利用 :let
來取得它(在這邊我們使用 form
變數來接住他)。
action
attribute 則是設定表格送出 (預設就是我們要用的 post) 的目標網址,這一次我們會使用 post 到 /notes
位置。
表格的欄位可以使用 <.input>
component 來產生,我們可以使用 field
attribute 來帶入要使用的欄位,這邊我們使用 form[:content]
,他就會幫我們產生相對應的 html 輸入欄位。
在這邊我們先不用太擔心 Phoenix 是怎麼幫我們把這些轉成最後在畫面上的 html,我們先專注在讓表格完成。
現在我們可以在 http://localhost:4000/notes/new
看到我們的表格了,但是如果我們輸入內容送出,會發現他抱怨在 NoteController 找不到 create 函式,這代表我們的表格送出成功了,下一回我們要建立 create 函式來處理送出的表格。