ToolJet Table 组件服务端搜索(Server Side Search)完整指南:从 SQL 查询到事件链路
【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet
本篇指南讲解如何在 ToolJet 的Table组件上实现服务端搜索(Server Side Search)。与客户端搜索不同,服务端搜索把搜索条件交给数据库在服务器端执行,搜索范围覆盖整个数据集而非仅当前已加载的行,适用于数据量大、需要保证数据一致性与安全性的场景。读完本文,你将掌握:如何搭建基础查询与 Table 数据绑定、如何启用服务端搜索属性、如何编写带searchText变量的 SQL、如何用 Search 事件驱动查询执行,以及如何通过 Loading State 提升交互体验。
服务端搜索与客户端搜索的区别
在开始配置之前,先明确两者的边界。ToolJet 官方文档(serverside-operations/overview.md)对服务端操作(server side operations)与客户端操作(client side operations)做了如下界定:
- 服务端操作:数据获取、过滤、排序、分页等任务在服务器(数据库)上执行,充分利用服务器资源,适合大数据集,加载更快、扩展性更好,同时有利于数据安全与完整性。
- 客户端操作:所有数据先一次性拉到浏览器,再在本地完成过滤与排序。交互实时性高、服务器负载低,但数据集较大时首屏加载与本地处理都会出现性能瓶颈。
因此,当你的表需要承载大型数据集、涉及复杂业务逻辑或对数据安全有要求时,应优先考虑服务端操作;ToolJet 的 Table 组件围绕搜索、排序、过滤、分页四类操作提供了完整的服务端支持(详见 serverside-operations 目录)。
从源码看,Table 组件的配置定义位于 frontend/src/AppBuilder/WidgetManager/widgets/table.js,其中serverSideSearch属性被定义为clientServerSwitch类型,即在 "Client side" 与 "Server side" 之间二选一:
serverSideSearch: { type: 'clientServerSwitch', displayName: 'Type', options: [ { displayName: 'Client side', value: 'clientSide' }, { displayName: 'Server side', value: 'serverSide' }, ], validation: { schema: { type: 'boolean' }, defaultValue: false, }, },defaultValue: false表示默认使用客户端搜索;切换为Server side后,Table 内部将停止对已加载数据的本地过滤,转而依赖暴露出的searchText变量把搜索词交给查询去处理。
准备工作:添加 Table 组件并绑定数据
实现服务端搜索前,先把 Table 组件放到画布并让它有数据可显示:
- 拖入组件:从右侧组件库中把Table组件拖到画布上。
- 创建数据查询:点击底部的查询面板(Query Panel),选择一个数据源并新建查询。本指南使用 ToolJet 内置的示例数据源(Postgres),查询语句如下:
SELECT * FROM public.sample_data_orders LIMIT 100该查询(即后续要配合搜索事件运行的getOrders查询)的作用是从sample_data_orders表取回 100 行订单数据作为 Table 的初始数据源。仓库的示例应用模板 server/templates/sample_app_def.json 中同样内置了这条查询,可以作为参考:
"query": "SELECT * FROM public.sample_data_orders\nLIMIT 100"- 绑定 Data 属性:选中 Table 组件,在属性面板的Data属性中填入
{{queries.getOrders.data}}(getOrders换成你的实际查询名),让表格展示查询返回的数据。
完成这三步后,Table 会渲染出 100 行示例订单数据,接下来就可以配置服务端搜索。
启用服务端搜索属性
在属性面板中执行以下操作:
- 找到 Table 组件属性中的Server side Search(位于 Search、Sort And Filter 相关分组内);
- 将该属性切换为Server side(默认是 Client side)。
此时,Table 组件不会再对已加载的 100 行数据做本地过滤,搜索行为完全交由你在查询中实现的 SQL 逻辑负责。
从源码层面看,服务端搜索的开关在 frontend/src/AppBuilder/Widgets/NewTable/_components/TableContainer/TableContainer.jsx 中通过useTableStore读取:
const serverSideSearch = useTableStore((state) => state.getTableProperties(id)?.serverSideSearch, shallow);该值随后被传入useTable与列构建逻辑;同时,TableContainer.jsx 中有一段关键逻辑——当服务端搜索开启且搜索词非空时,自动把分页重置到第一页:
useEffect(() => { if (serverSideSearch && globalFilter?.trim() !== '') { setPagination((prev) => ({ ...prev, pageIndex: 0 })); } }, [globalFilter, serverSideSearch, setPagination]);这意味着每次发起新的搜索,结果都会从第 1 页开始展示,避免停留在旧搜索结果的页码上。属性面板中该开关的显示条件在 frontend/src/AppBuilder/RightSideBar/Inspector/Components/Table/Table.jsx 中由displaySearchBox推导,保证只有启用搜索框时才展示该配置项。
编写服务端搜索查询:searchText 变量的用法
启用服务端搜索后,编辑刚才的查询,把搜索条件写进 SQL。ToolJet 会把用户在 Table 搜索框中输入的内容实时暴露为组件变量{{components.table1.searchText}}(table1换成你的 Table 组件名),因此 SQL 可以这样写:
SELECT * FROM public.sample_data_orders WHERE city ILIKE '%{{components.table1.searchText}}%' OR country ILIKE '%{{components.table1.searchText}}%' OR state ILIKE '%{{components.table1.searchText}}%' LIMIT 100该查询在数据库端对city、country、state三个文本列做ILIKE模糊匹配,从而在整个数据集上完成搜索,而不是只搜索当前页的 100 行数据。要点如下:
ILIKE是 PostgreSQL 的大小写不敏感模糊匹配操作符,配合%...%通配符实现包含式匹配;{{components.table1.searchText}}中的table1必须替换为你实际的 Table 组件名;- 用
OR连接多个列,即可扩展搜索范围(例如再加customer_name、order_id等列); - 保留
LIMIT 100防止全表扫描结果过大,配合分页使用效果更佳。
searchText变量的来源可以从源码得到印证:Table 组件的暴露变量定义在 frontend/src/AppBuilder/WidgetManager/widgets/table.js,其中包含searchText: ''这一初始值。在运行时,TableExposedVariables.jsx 将搜索框的globalFilter状态同步为暴露变量:
// Expose search text useEffect(() => { setExposedVariables({ searchText }); mounted && fireEvent('onSearch'); }, [searchText, setExposedVariables, fireEvent]);也就是说,用户在搜索框每输入一个字符,searchText都会更新,并同时触发onSearch事件——这正是下一步要挂接事件处理器的原因。
挂接事件处理器:Search 事件 → Run Query
仅有 SQL 还不够,还需要让 Table 组件在每次搜索时主动执行该查询。做法是给 Table 组件添加一个事件处理器(Event Handler):
- Event(事件):
Search - Action(动作):
Run Query - Query(查询):选择你编写了搜索 SQL 的那个查询(如
getOrders)
配置完成后,每当用户在搜索框中输入内容(searchText变化),就会触发Search事件 → 执行Run Query动作 → 重新运行查询并携带最新的searchText参数 → 查询返回过滤后的数据并刷新 Table。
这条链路在源码中的闭环如下:
- 用户在搜索框输入,更新
globalFilter; - TableExposedVariables.jsx 同步
searchText暴露变量并调用fireEvent('onSearch')触发 Search 事件; - 事件处理器执行 Run Query,查询中的
{{components.table1.searchText}}取到最新搜索词; - 数据库执行带
ILIKE条件的 SQL,返回过滤结果; - Table 重新渲染,同时(如已开启)分页重置到第 1 页。
关于onSearch事件与fireEvent的调用,还可参考 TableContainer 中buildTableColumn的调用(buildTableColumn.js),搜索框的globalFilter状态贯穿了列构建与事件触发全流程。
添加 Loading State,优化搜索反馈
搜索是异步操作,查询执行期间需要给用户明确的加载反馈。配置步骤如下:
- 打开 Table 组件属性面板中的Additional Actions(附加操作)区域;
- 点击Loading State旁边的fx图标;
- 在表达式输入框中填入
{{queries.getOrders.isLoading}}(getOrders替换为你的查询名)。
{{queries.getOrders.isLoading}}是查询的运行状态标志:查询执行期间为true、执行结束后为false。绑定到 Loading State 后,表格会在查询进行时显示加载指示器,避免用户误以为搜索无响应。Table 组件的isLoading暴露变量同样定义在 table.js 的exposedVariables中(初始值false),属性面板中的loadingState属性默认值也为false。
完整实现链路回顾
至此,ToolJet Table 组件的服务端搜索已完整实现。把整个流程串起来:
- 数据准备:Table 组件绑定
{{queries.getOrders.data}},初始展示 100 行订单数据; - 开启服务端模式:属性面板中把 Server side Search 切换为 Server side;
- 编写服务端 SQL:用
WHERE city/country/state ILIKE '%{{components.table1.searchText}}%'在数据库端完成全量数据搜索; - 事件驱动刷新:Search 事件 → Run Query 动作,每次搜索自动重跑查询;
- 加载反馈:Loading State 绑定
{{queries.getOrders.isLoading}},查询期间显示加载状态。
现在,当用户在 Table 搜索框中输入内容时,查询会在服务器端执行,搜索范围覆盖整个数据集(而非仅当前加载行),这正是服务端搜索的核心价值。若要进一步优化,可结合服务端排序(sort.md)、服务端过滤(filter.md)与服务端分页(pagination.md)构成完整的服务端操作体系,相关实现均可在 frontend/src/AppBuilder/WidgetManager/widgets/table.js 的serverSidePagination、serverSideSort、serverSideFilter配置定义中看到它们的并列关系。
【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考