Dynamics 365中Promise回调后调用setDisabled()无效问题
问题根因
和JS线程无关,问题出在Dynamics 365客户端框架的上下文生命周期管理,以及代码里的上下文用法错误:
- 可编辑子网格的行、属性、字段控件上下文都是短生命周期对象,仅在事件触发的同步执行阶段和UI保持绑定。同步栈执行完成后,框架会销毁这些临时对象的UI关联,异步回调里持有的全局
gridContext只是内存里的游离对象,调用setDisabled()只会修改对象内存值,根本不会触发界面更新。 - 代码里
context.getFormContext()的用法本身就是错的:子网格行选中事件里调用这个方法拿到的是主表单上下文,不是子网格行上下文。同步场景下代码能生效,只是因为事件触发瞬间框架给上下文临时挂载了行数据关联,一旦进入异步阶段这个临时关联就会断开。 - Async/Await写法复现同样问题的原因也很简单:
await会让出执行权,等异步逻辑恢复执行时,之前缓存的行上下文早就失效了,和语法本身没有关系。
修复方案
核心原则:绝对不要缓存子网格的行、字段、属性类上下文供异步逻辑使用。异步场景下只需要缓存长期有效的网格控件引用、以及目标行的ID(静态值不会失效),等异步操作完成后,重新从最新的网格实例上拉取目标行和字段的引用,再执行操作即可。
修复后的可直接运行代码:
function onGridRowSelected(executionContext) { // 只缓存长期有效的网格控件实例,以及选中行的静态ID const gridControl = executionContext.getEventSource(); const selectedRows = gridControl.getSelectedRows(); const selectedRowIds = []; selectedRows.forEach(row => { // 去除GUID两端的大括号,匹配后续行查找的格式要求 selectedRowIds.push(row.getId().replace(/[{}]/g, "")); }); // 执行异步查询 Xrm.WebApi.retrieveMultipleRecords("ms_approvalquery", "?$select=ms_responsetext&$top=1&$orderby=createdon desc") .then(result => { const needDisable = !!(result.entities.length && result.entities[0].ms_responsetext); // 异步返回后,从网格控件拉取最新的行实例列表 const latestRows = gridControl.getAllRows(); selectedRowIds.forEach(rowId => { const targetRow = latestRows.get(rowId); // 行已被切换、刷新不存在时直接跳过 if (!targetRow) return; // 遍历行内所有字段设置禁用状态 targetRow.data.entity.attributes.forEach(attr => { attr.controls.forEach(fieldControl => { fieldControl.setDisabled(needDisable); }) }) }) }) }
注意事项
- 所有可编辑子网格的行级、字段级操作,只要涉及异步逻辑,都要遵循“存静态ID、异步后重新取实例”的模式,不要缓存任何和行渲染绑定的临时对象。
- 如果设置完状态后偶现UI不更新,可以在遍历完字段后调用
gridControl.refresh()触发网格重绘,常规场景下不需要额外调用。 - 上述代码已经兼容多行选中场景,会自动处理所有选中行的字段状态。
内容的提问来源于stack exchange,提问作者Bitten Fleax
相关产品推荐
相关产品推荐

