学完这一课,点击“Add User”按钮会打开对话框,提交表单后客户会被添加到表格中,对话框也会自动关闭。这是让界面响应用户操作的第一步,也是最核心的一步。
事件处理器是定义在 State 中、并加上 @rx.event 装饰器的方法。只有事件处理器才能修改状态。
class State(rx.State):
users: list[User] = [...]
@rx.event
def add_user(self, form_data: dict):
self.users.append(User(**form_data))self.users 不是 Var,而是实实在在的列表,所以 append、len、if、for,甚至外部库都可以随便用。self.users 后,Reflex 会检测到变化并发送给浏览器,表格和徽章会自动重新渲染。我们不需要写任何直接修改界面的代码。User(**form_data) 会把字典解包成关键字参数传入:{"name": "Kim", "email": "...", "gender": "Male"} → User(name="Kim", email="...", gender="Male")。讲师笔记 — 请和第3课的表格对照讲解:“在界面函数里是 Var,所以不能用 len;在事件处理器里是真实的值,所以可以用 len。”理解了这个对比,Reflex 就算懂了一半。
rx.form(
rx.flex(
rx.input(placeholder="User Name", name="name", required=True),
rx.input(placeholder="user@reflex.dev", name="email"),
rx.select(
["Male", "Female"],
placeholder="Select gender",
default_value="Male",
name="gender",
custom_attrs={"aria-label": "Gender"},
),
rx.flex(
rx.dialog.close(
rx.button("Cancel", type="button", variant="soft", color_scheme="gray"),
),
rx.button("Submit", type="submit"),
spacing="3",
justify="end",
),
direction="column",
spacing="4",
),
on_submit=State.add_user,
reset_on_submit=False,
)name= 是把各部分串起来的关键。提交时,每个输入框的 name 会成为键,组成 form_data 字典。它必须和 User 的字段名(name、email、gender)完全一致,User(**form_data) 才能正常工作。on_submit=State.add_user 传入的是类上的处理器,并且不调用它。表单生成的字典会自动填入 form_data 参数。required=True 让浏览器在提交前拦住空白输入。type="button" 很重要。表单中的按钮默认是 submit,如果漏写了,点“取消”也会添加客户。rx.flex 是 display="flex" 的盒子。direction="column" 表示纵向排列,spacing 是间距。custom_attrs 用于直接附加 Reflex 不认识的 HTML 属性。这里加的是给屏幕阅读器用的标签。def add_customer_button() -> rx.Component:
return rx.dialog.root(
rx.dialog.trigger( # 点击后打开对话框的元素
rx.button(
rx.icon("plus", size=18),
rx.text("Add User", size="2"),
),
),
rx.dialog.content( # 对话框主体
rx.dialog.title("Add New User"),
rx.dialog.description("Fill the form with the user's info"),
rx.form(...), # 4-2 中的表单
max_width="450px",
),
)rx.icon("plus") 直接使用 Lucide 的图标名称。add_customer_button() 的形式放到页头右侧(标题 box 之后,同一个父元素内)。在浏览器中确认 — 页头最右侧出现一个蓝色的“+ Add User”按钮。
localhost:3000
动手试试Your customer directory and audience breakdown.
点击按钮后,背景变暗并弹出对话框,性别默认选中 Male。可以在上面的预览中亲自输入内容,然后点击 Submit 试试。
做到这里运行一下,会发现一个问题:客户添加成功了,但对话框没有关闭。这是因为对话框的打开/关闭状态完全由浏览器自己管理,服务器上的事件处理器没有办法通知它关闭。
解决办法是把“是否打开”这件事交给 State 管理。这种做法叫做受控组件(controlled component)模式。
class State(rx.State):
users: list[User] = [...]
add_user_dialog_open: bool = False # 1) 把打开状态放进 State
@rx.event
def set_add_user_dialog_open(self, open_: bool): # 2) 自己定义 setter
self.add_user_dialog_open = open_
@rx.event
def add_user(self, form_data: dict):
self.users.append(User(**form_data))
self.add_user_dialog_open = False # 3) 提交后关闭rx.dialog.root(
...,
open=State.add_user_dialog_open, # 由状态决定是否打开
on_open_change=State.set_add_user_dialog_open, # 用户打开或关闭时同步到状态
)open= 而漏掉 on_open_change,点击触发按钮时状态不会变化,对话框就打不开。这两者必须成对出现。on_open_change 会在打开时把 True、关闭时(Cancel、ESC、点击外部)把 False 传给事件处理器。State.set_变量名,但 0.10 不会自动生成这类方法,所以要像上面那样自己定义。open_,是为了避免和 Python 内置函数 open 重名的惯例写法。reset_on_submit=False,但对话框关闭后其内容会从页面上移除,再次打开时重新渲染,所以输入框会恢复为空。{"name": ..., "email": ..., "gender": ...}。add_user(form_data)。users、add_user_dialog_open)会被发回浏览器。在浏览器中确认 — 应用完 4-4 的修改后点击 Submit,对话框会关闭,表格新增第 3 行,徽章变为 3。如果对话框仍然开着,请再检查一下 4-4 中的 open= 和 on_open_change=。
localhost:3000
动手试试Your customer directory and audience breakdown.
"Other"。type="email",看看提交格式错误的内容时会发生什么。return rx.toast.error("该客户已存在")。添加成功时也试着返回 rx.toast.success(...)。name=、form_data 与 User 字段之间的关系。订阅新课时和新课程通知
本课程新增课时或推出后续课程时,我们会发邮件通知你。
问答
关于本课有任何疑问都可以在这里留言。所有语言版本页面的提问会汇集在一起。