表单处理
Nex 使得表单处理变得异常简单。得益于声明式交互的设计,你可以轻松实现异步表单提交,且无需手动编写复杂的 AJAX 逻辑。此外,Nex 内置了对文件上传的原生支持。
1. 基础表单提交
在 Nex 中,你只需要给 <form> 标签添加 hx-post 属性。
示例:留言板
创建 src/pages/guestbook.ex:
defmodule MyApp.Pages.Guestbook do
use Nex
def mount(_params) do
%{messages: Nex.Store.get(:messages, [])}
end
def render(assigns) do
~H"""
<div class="max-w-md mx-auto p-6">
<h1 class="text-2xl font-bold mb-4">留言板</h1>
<!-- 异步提交到 save_message Action -->
<form hx-post="/save_message" hx-target="#message-list" hx-swap="afterbegin" class="mb-8">
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700">姓名</label>
<input type="text" name="name" required class="mt-1 block w-full border rounded-md p-2">
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700">内容</label>
<textarea name="content" required class="mt-1 block w-full border rounded-md p-2"></textarea>
</div>
<button type="submit" class="w-full bg-black text-white py-2 rounded-md">提交留言</button>
</form>
<div id="message-list" class="space-y-4">
<%= for msg <- @messages do %>
<.message_item name={msg.name} content={msg.content} />
<% end %>
</div>
</div>
"""
end
# Action 接收表单参数 Map
def save_message(%{"name" => name, "content" => content}) do
new_msg = %{name: name, content: content}
# 保存到状态
Nex.Store.update(:messages, [], &[new_msg | &1])
# 返回一个 HTML 片段用于插入列表
assigns = %{name: name, content: content}
~H"<.message_item name={@name} content={@content} />"
end
# 定义一个局部组件
defp message_item(assigns) do
~H"""
<div class="p-4 bg-gray-50 rounded-lg border">
<div class="font-bold text-sm">{@name}</div>
<div class="text-gray-600 mt-1">{@content}</div>
</div>
"""
end
end
2. 获取参数
Action 函数接收一个 Map,其中的键对应表单控件的 name 属性。
- 如果表单包含多个同名的 input,Nex 会将其封装为列表。
- 对于空输入,键通常依然存在,但值可能为空字符串。
3. 文件上传 (Multipart)
Nex 原生支持 multipart/form-data。当你在表单中包含 <input type="file"> 时,Nex 会自动处理上传。
示例:头像上传
def upload_avatar(%{"avatar" => %Plug.Upload{path: path, filename: name}}) do
# Nex 自动将上传文件封装为 Plug.Upload 结构
# path: 临时文件路径
# filename: 原始文件名
# 你可以将文件移动到持久存储
File.cp!(path, "priv/static/uploads/#{name}")
"上传成功:#{name}"
end
练习:Todo List
尝试结合前两章的知识,写一个 Todo List:
- 表单输入 Todo 内容。
-
Action
add_todo处理提交,并返回一个新的 Todo 条目 HTML。 -
每个 Todo 条目带有一个“删除”按钮,使用
hx-delete调用delete_todo。