
本教程探讨如何在 docassemble 中实现国家与州/省份的动态联级下拉菜单。针对用户选择国家后,实时更新同一页面上州/省份列表的需求,文章将介绍两种主要策略:利用 `input type: ajax` 动态获取选项,以及通过 `background_response_refresh` 实现页面刷新以更新所有相关字段,并分析各自的优缺点及适用场景。
引言:动态联级选择的需求
在交互式表单设计中,一个常见需求是实现联级选择(Dependent Dropdowns),例如根据用户选择的国家,动态地更新后续的州、省或地区列表。Docassemble 提供了 countries_list() 和 states_list() 等强大函数来处理地理数据,但在默认情况下,这些列表通常在页面加载时确定,或通过多页问答流程实现。当需要在一个页面上实时更新这些选项,甚至包括字段标签时,就需要采用更高级的动态交互技术。
最初,我们可以通过将国家和州/省份的选择放在不同的问题(即不同的页面)来实现。例如:
--- question: | 您居住在哪个国家? fields: - Country: jurisdiction.country input type: combobox code: countries_list() --- question: | 您居住在哪个${ subdivision_type(jurisdiction.country) }? fields: - ${ subdivision_type(jurisdiction.country) }: jurisdiction.state input type: combobox code: states_list(jurisdiction.country) ---
这种方法在用户完成国家选择并进入下一个问题时,会根据 jurisdiction.country 的值生成相应的州/省份列表和字段标签。然而,如果希望在同一个页面上,用户改变国家选择后,州/省份列表和标签能即时更新,上述方法就无法满足需求。
策略一:使用 ajax 动态更新选项列表
Docassemble 提供了 input type: ajax 机制,允许字段在不刷新整个页面的情况下,通过后台请求动态获取其选项。这种方法适用于只需要更新选项列表,而不需要更新字段标签的场景。
工作原理
当一个字段被设置为 input type: ajax 时,其 code 属性中定义的逻辑会在后台执行,并将返回的结果作为该字段的选项。如果该字段的 code 依赖于页面上其他字段的值(例如,州列表依赖于国家选择),那么当依赖字段的值发生变化时,Docassemble 会自动触发 AJAX 请求来重新计算并更新当前字段的选项。
示例代码
--- question: | 请选择您的居住地 fields: - Country: jurisdiction.country input type: combobox code: countries_list() - State: jurisdiction.state # 注意:这里的标签是静态的 input type: ajax code: states_list(jurisdiction.country) # 依赖于 jurisdiction.country # 如果 jurisdiction.country 发生变化,此字段会自动通过 AJAX 重新加载其选项 ---
在上述示例中:
- jurisdiction.country 字段是一个普通的下拉框,显示国家列表。
- jurisdiction.state 字段被设置为 input type: ajax。它的 code states_list(jurisdiction.country) 会在后台执行,获取与所选国家对应的州/省份列表。
- 当用户在“国家”下拉框中选择一个新国家时,jurisdiction.state 字段会自动触发一个 AJAX 请求,重新计算 states_list(jurisdiction.country) 并更新其选项列表,而无需刷新整个页面。
注意事项
- 字段标签的限制: 使用 input type: ajax 方式,虽然可以动态更新选项列表,但字段的标签(label)本身不会自动更新。例如,subdivision_type(jurisdiction.country) 这样的动态标签在用户改变国家后不会实时变化。
- 自定义 javaScript: 如果确实需要动态更新字段标签,你可能需要编写自定义的 javascript 代码来监听国家选择的变化,并手动修改州/省份字段的 html 标签。这需要对 Docassemble 的前端结构和 JavaScript 有一定了解。
策略二:利用 background_response_refresh 实现页面刷新
如果不仅需要更新选项列表,还需要动态更新字段标签(例如将“State”更新为“Province”),并且可以接受一次完整的页面刷新,那么使用 background_response_refresh() 是一个更直接的解决方案。
工作原理
background_response_refresh() 函数会触发一次后台响应,导致整个当前页面被重新渲染。当页面重新渲染时,所有依赖于已更新变量(如 jurisdiction.country)的字段和标签都会被重新计算和显示。
示例代码
--- question: | 请选择您的居住地 fields: - Country: jurisdiction.country input type: combobox code: countries_list() onchange: | # 当国家选择发生变化时,触发页面刷新 background_response_refresh() - ${ subdivision_type(jurisdiction.country) }: jurisdiction.state input type: combobox code: states_list(jurisdiction.country) ---
在上述示例中:
- jurisdiction.country 字段的 onchange 事件被配置为调用 background_response_refresh()。
- 当用户在“国家”下拉框中选择一个新国家时,onchange 事件会立即触发,导致整个页面刷新。
- 页面刷新后,jurisdiction.country 的新值会生效,jurisdiction.state 的 code (states_list(jurisdiction.country)) 和其字段标签 (subdivision_type(jurisdiction.country)) 都会根据新的国家值重新计算并显示。
注意事项
- 用户体验: 这种方法会导致一次明显的页面闪烁或重新加载,相比 AJAX 的无缝更新,用户体验可能稍逊。
- 性能考量: 对于包含大量内容或复杂计算的页面,频繁的页面刷新可能会影响性能。
总结与选择
在 Docassemble 中实现国家与州/省份的动态联级选择,主要取决于你的具体需求和对用户体验的权衡:
- 如果只需动态更新选项列表,且不关心字段标签的动态变化,或者愿意通过自定义 JavaScript 处理标签更新: 推荐使用 input type: ajax。它提供了更流畅的用户体验,避免了页面刷新。
- 如果需要同时动态更新选项列表和字段标签,并且可以接受一次页面刷新: 推荐使用 background_response_refresh()。这种方法实现起来更简单,但会带来页面重新加载。
在实际项目中,你可以根据表单的复杂性、用户对响应速度的要求以及开发成本来选择最合适的策略。对于大多数需要动态联级选择的场景,Docassemble 提供的这两种机制都能有效解决问题。


