在完成準備放新文章的 modal 後,其實就是在裡面加上 <.form >
就可以了,但是有幾個東西需要先加進 assigns 裡
更新 BlogWeb.PostLive.Form 的 update, 我們要在打開這個 LiveComponent 的時候把 changeset 加進來lib/blog_web/live/post_live/form.ex
def update(_assigns, socket) do
post = %Posts.Post{}
changeset = Posts.change_post(post)
{:ok, assign(socket, %{post: post, changeset: changeset})}
end
這裡與傳統的 MVC 路線類似,用 <.form > 帶入 changeset 把表格畫出來
這邊來新增一個 component 裝表格
跟傳統不同的是,LiveView 這邊使用 phx-submit 來呼叫送出事件到 LiveView,而不是重新發一個 Post 連線
我們待會可以用 handle_event 來把 submit 接起來處理表格送出。
另外在 LiveView 有 phx-change 這個 binding 可以讓我們決定表格有改動的時候我們要做什麼,通常在這邊我們拿來即時驗證表格
這邊要注意的是,因為我們這次把表格在 LiveComponent 處理,所以必須要加上 phx-target={@myself} 告訴 LiveView 這個事件是由 LiveComponent 處理,
如果沒有寫的話,會送去母層的 Index LiveView,其實要的話也是可以過去處理,但我們這次想讓表格的東西集中在一起。
def post_form(assigns) do
~H"""
<.form let={f} for={@changeset} phx-change="validate" phx-submit="submit" phx-target={@myself}>
<%= label(f, :title) %>
<%= text_input(f, :title) %>
<%= error_tag(f, :title) %>
<%= label(f, :body) %>
<%= textarea(f, :body) %>
<%= error_tag(f, :body) %>
<div>
<%= submit("送出") %>
</div>
</.form>
"""
end
把新的 component 放進 modal 裡
def render(assigns) do
~H"""
<div>
<.modal>
<h1>新增文章</h1>
<.post_form changeset={@changeset} myself={@myself} />
</.modal>
</div>
"""
end
這樣就可以顯示表格了
但我們會發現輸入或是送出都會壞掉導致 LiveView 重新整理
這是因為我們還沒有寫要怎麼處理那些事件
這邊做的事情其實就是把表格準備送出的內容套進 changeset 裡面,讓他檢查一下我們之前寫的規則
要記得把 changeset 的 action 改成 validate 這樣畫面上才會顯示檢查的錯誤項目
def handle_event("validate", %{"post" => attrs}, socket) do
changeset =
Posts.change_post(socket.assigns.post, attrs)
|> Map.put(:action, :validate)
{:noreply, assign(socket, %{changeset: changeset})}
end
就這樣我們就完成了即時的錯誤回饋,這個在以前的作法要弄是很麻煩的。
這裡跟我們之前在 PostController create 動作做的事情非常類似
我們使用 Posts.create_post 來建立文章
如果建立成功,我們用 push_patch 來回到 Index 頁面並重整一次 assigns
如果失敗,就把回傳的 changeset 再丟回 assigns
def handle_event("submit", %{"post" => attrs}, socket) do
socket =
case Posts.create_post(attrs) do
{:ok, _post} ->
push_patch(socket, to: Routes.post_index_path(socket, :index))
{:error, changeset} ->
assign(socket, %{changeset: changeset})
end
{:noreply, socket}
end
如果我們使用 push_patch 來重新整理 BlogWeb.PostLive.Index 頁面的話,
因為連線不會中斷,所以他會去呼叫另一個 callback handle_params
文件
這個 handle_params 正常會在 mount 之後執行,如果沒有定義就跳過,
那如果我們剛剛直接用 push_patch 回到頁面的話,他會略過 mount 直接找 handle_params
所以我們要調整一下 BlogWeb.PostLive.Index
def mount(_params, _session, socket) do
{:ok, socket}
end
def handle_params(_params, _uri, socket) do
{:noreply, assign(socket, %{posts: Posts.list_posts(), form: false})}
end
好了之後我們整個循環就完成了