NiceGUI 可编辑 AG Grid 实战:构建支持增、删、改行的数据表格
【免费下载链接】niceguiCreate web-based user interfaces with Python. The nice way.项目地址: https://gitcode.com/GitHub_Trending/ni/nicegui
导读
本指南以 NiceGUI 仓库中的 examples/editable_ag_grid 示例为核心,完整讲解如何基于ui.aggrid构建一个支持新增行、单元格内联编辑、批量删除选中行的数据表格,并深入源码剖析options数据同步、事件转发与行选择 API 的底层机制。读完本文,你将能够在自己的 NiceGUI 应用中复现一个开箱即用的可编辑表格,并掌握让服务端数据与前端网格保持一致的关键技巧。
示例总览
AG Grid 是功能强大的 JavaScript 数据网格库,NiceGUI 通过ui.aggrid元素将其封装为 Python 组件:全部网格配置以字典形式传入,Python 端通过操作options字典与浏览器端保持同步。本文对应的完整示例位于 examples/editable_ag_grid/main.py,运行方式与仓库中其他示例一致:
python main.py启动后浏览器打开 NiceGUI 默认地址(通常为http://localhost:8080),即可看到下图所示的表格界面:
示例实现了三项核心能力:双击单元格修改数据、点击New row追加行、选中若干行后点击Delete selected批量删除,每一步操作都会通过ui.notify在页面右上角给出反馈。
一、构建可编辑网格:从一段核心配置开始
先看示例中创建网格的部分(main.py):
aggrid = ui.aggrid({ 'columnDefs': [ {'field': 'name', 'editable': True, 'sortable': True}, {'field': 'age', 'editable': True}, {'field': 'id'}, ], 'rowData': [ {'id': 0, 'name': 'Alice', 'age': 18}, {'id': 1, 'name': 'Bob', 'age': 21}, {'id': 2, 'name': 'Carol', 'age': 20}, ], 'rowSelection': {'mode': 'multiRow'}, 'stopEditingWhenCellsLoseFocus': True, }).on('cellValueChanged', handle_cell_value_change)对配置项逐一说明:
| 配置键 | 作用 | 示例值说明 |
|---|---|---|
columnDefs | 定义列及每列的属性 | editable: True开启单元格编辑;sortable: True允许点击表头排序;id列不编辑、仅作行标识 |
rowData | 网格初始数据,列表中的每个字典对应一行 | 这里初始化了三行数据 |
rowSelection | 行选择模式 | {'mode': 'multiRow'}表示支持多行选择(同时选中多行) |
stopEditingWhenCellsLoseFocus | 编辑状态控制 | 置True后,单元格在失去焦点时即结束编辑并提交新值 |
其中stopEditingWhenCellsLoseFocus值得特别留意。从 aggrid.py 的文档注释可以看到,编辑中的单元格只有在退出编辑模式后,其数据才会被回传——而"失去焦点即退出编辑"这个行为必须显式开启。若不设置该项,用户点击网格外区域时编辑不会结束,服务端可能一直拿不到新值。
二、三种行操作:新增、编辑、删除的完整实现
2.1 新增行(add_row)
def add_row(): new_id = max((dx['id'] for dx in aggrid.options['rowData']), default=-1) + 1 aggrid.options['rowData'].append({'id': new_id, 'name': 'New name', 'age': None}) ui.notify(f'Added row with ID {new_id}')关键点:直接修改aggrid.options['rowData']这个列表。options是AgGrid暴露的受监控字典(见源码 aggrid.py),对其中数据的修改会被 NiceGUI 检测到并自动推送到浏览器端刷新网格。新增行时用max(...) + 1生成自增id,保证新行 id 不与现有行冲突。
补充:
options字典在创建时还会被自动注入theme(默认'quartz')等字段,详见 aggrid.py;auto_size_columns未显式设置时,会按"列不使用flex则自动撑满网格宽度"的规则生成autoSizeStrategy。
2.2 单元格编辑与事件回传(handle_cell_value_change)
def handle_cell_value_change(e): new_row = e.args['data'] ui.notify(f'Updated row to: {e.args["data"]}') aggrid.options['rowData'][:] = [row | new_row if row['id'] == new_row['id'] else row for row in aggrid.options['rowData']]用户编辑完成后触发cellValueChanged事件,事件对象e.args中包含 AG Grid 回传的整行数据(data字段)。回调中做两件事:
- 用
ui.notify给出编辑反馈; - 用编辑后的新行数据替换
rowData中 id 匹配的旧行——row | new_row是 Python 3.9+ 的字典合并语法,new_row的字段会覆盖旧行同名字段。通过切片赋值rowData[:] = ...原地更新列表,确保网格能感知变更并刷新。
这一"事件驱动 + 回写 options"的模式,正是可编辑网格保持前后端数据一致性的核心套路。
2.3 删除选中行(delete_selected)
async def delete_selected(): selected_id = [row['id'] for row in await aggrid.get_selected_rows()] aggrid.options['rowData'][:] = [row for row in aggrid.options['rowData'] if row['id'] not in selected_id] ui.notify(f'Deleted row with ID {selected_id}')删除操作分两步:
await aggrid.get_selected_rows()从浏览器端取回当前所有选中行的数据;- 用列表推导式过滤掉 id 在选中集合中的行,再通过切片赋值写回
rowData。
get_selected_rows是AgGrid提供的内置异步方法,其实现位于 aggrid.py,底层调用 AG Grid API 的getSelectedRows方法;配套的get_selected_row(aggrid.py)则在单行选择模式下返回第一行或None。官方测试 tests/test_aggrid.py 对多选与单选两种取回方式均有验证。
最后,两个按钮把三个操作串起来:
ui.button('Delete selected', on_click=delete_selected) ui.button('New row', on_click=add_row)三、源码原理:事件如何从浏览器到达 Python
示例中.on('cellValueChanged', handle_cell_value_change)能生效,依赖 NiceGUI 为 AG Grid 建立的事件转发链路。从 aggrid.js 可以看到,网格创建后立即注册了全局监听器:
this.api = AgGrid.createGrid(this.$el, this.gridOptions); this.api.addGlobalListener(this.handle_event);handle_event(aggrid.js)会把 AG Grid 事件参数挑选出可安全序列化的字段(如data、value、oldValue、newValue、rowIndex、colId等)打包后通过$emit发送给 Python 端,Python 端即可通过.on('cellValueChanged', ...)订阅。
需要留意的是:官方文档指出,
rowClicked等部分事件的参数包含循环引用,直接注册会因序列化失败而无法工作。此时可在.on()中显式限定事件参数,例如.on('rowClicked', lambda event: ui.notify(f'Row: {event.args}'), ['data']),只序列化data字段即可规避(详见 aggrid_documentation.py)。
四、进阶能力:把可编辑网格用得更好
4.1 读取客户端编辑结果:get_client_data / load_client_data
如果不想逐个监听cellValueChanged事件,可在任意时刻批量取回客户端全部(或筛选/排序后)的数据:
data = await grid.get_client_data() # 全部行(默认顺序) sorted_data = await grid.get_client_data(method='filtered_sorted') # 过滤并排序后的行 grid.load_client_data() # 直接将客户端数据回写为 options['rowData']get_client_data支持all_unsorted、filtered_unsorted、filtered_sorted、leaf四种取值方式(实现见 aggrid.py),其测试用例在 tests/test_aggrid.py 中验证了默认顺序与排序后的取回结果。同样地,它的文档注释再次强调:编辑中的单元格在退出编辑前不会更新数据,必要时请开启stopEditingWhenCellsLoseFocus。
4.2 调用 AG Grid API:run_grid_method / run_row_method
run_grid_method可在 Python 端调用任意 AG Grid 网格 API(如selectAll、setColumnsVisible、applyTransaction),run_row_method则针对指定行调用(需先用getRowId定义行 id)。二者均可await以取回返回值(aggrid.py),官方文档示例与测试均有覆盖(如 tests/test_aggrid.py 中通过run_row_method('Alice', 'setDataValue', 'age', 42)直接改写指定单元格)。
关于新增行的进阶做法:直接修改
rowData会让客户端整体重建网格,正在编辑的单元格内容会丢失。若需保留未保存的编辑,可以结合grid.props.suspend_updates()与服务端applyTransaction事务 API 实现"无重建"新增,官方文档 aggrid_documentation.py 提供了完整示例。
4.3 其他实用配置
- 列定义:
headerName自定义表头、hide隐藏列、filter/floatingFilter启用迷你过滤器、':getRowHeight'传 JS 函数实现动态行高(见 aggrid_documentation.py); - 数据源:
ui.aggrid.from_pandas(df)与ui.aggrid.from_polars(df)可直接从 DataFrame 创建网格(aggrid.py),非 UTF-8/特殊类型列会被自动转为字符串; - 主题与模块:构造函数支持
theme(quartz/balham/material/alpine,默认quartz)与modules(community/enterprise或自定义模块列表),当前 NiceGUI 内置的 AG Grid 版本为ui.aggrid.VERSION = '34.2.0'(aggrid.py)。
五、常见问题速查
- 改了
rowData但界面没刷新:务必使用rowData[:] = ...原地替换或直接append,避免整体重新赋值整个options字典(后者可能触发不必要的全量重建)。 - 编辑后的值没被服务端感知:确认单元格已退出编辑模式,并优先开启
stopEditingWhenCellsLoseFocus: True;需要批量取回时使用get_client_data。 rowClicked等事件不触发:事件参数含循环引用导致序列化失败,用.on(event, handler, ['data'])限定参数范围。- 行 id 冲突:新增行时基于
max(id) + 1生成新 id,删除时按 id 过滤,保证增删操作始终以id为唯一键对齐前后端数据。
结语
通过本文,你已掌握 NiceGUI 中可编辑 AG Grid 的完整实现路径:用columnDefs/rowData声明网格、用editable开启单元格编辑、用cellValueChanged事件 + 回写options完成数据同步,再用get_selected_rows与列表过滤实现批量删除。这套模式可直接迁移到任何需要前端表格交互的 Python 应用场景中,结合 tests/test_aggrid.py 与 aggrid_documentation.py 中的更多示例,你可以继续探索过滤、排序、复杂对象、事务更新等高级用法。
【免费下载链接】niceguiCreate web-based user interfaces with Python. The nice way.项目地址: https://gitcode.com/GitHub_Trending/ni/nicegui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考