调用StateHasChanged后Blazor有序列表仍未更新问题
Blazor集成Sortable.js排序失效问题解决方案
问题核心
Sortable.js拖拽后已直接修改DOM结构,导致Blazor虚拟DOM与实际DOM不一致;同时OnSort方法未正确更新Item的OrderIndex属性,仅重新排序集合无法触发Blazor的变更检测,最终DOM列表未同步更新。
具体修复步骤
1. 阻止Sortable.js修改DOM,由Blazor全权控制渲染
修改JS初始化代码,在拖拽结束后先还原Sortable移动的DOM元素,避免与Blazor渲染冲突:
window.initializeSortable = (container, blazorComponent, items, options) => { const common = { onEnd: async function (evt) { // 还原Sortable移动的DOM,保证Blazor渲染时DOM初始状态正确 evt.item.parentNode.insertBefore(evt.item, evt.oldIndex >= evt.newIndex ? evt.futureIndex : evt.futureIndex.nextSibling); // 调用Blazor方法处理排序逻辑 await blazorComponent.invokeMethodAsync('UpdateItemOrder', evt.oldIndex, evt.newIndex); }, }; const init = Object.assign({}, { animation: 150 }, options, common); if (container != null) { Sortable.create(container, init); container.__blazor_component = blazorComponent; } }; // 添加销毁Sortable的方法(用于组件参数变更时重置) window.destroySortable = (container) => { if (container?.sortable) { container.sortable.destroy(); } };
2. 正确更新Item的OrderIndex值
修改OnSort方法,根据拖拽的索引变更实际调整每个Item的OrderIndex,并更新集合引用触发Blazor检测:
public async Task OnSort(SortChangedArgs sortChangedArgs) { var oldIndex = sortChangedArgs.OldIndex; var newIndex = sortChangedArgs.NewIndex; var items = ViewModel.VerificationItems.ToList(); var movedItem = items[oldIndex]; // 调整项在集合中的位置 items.RemoveAt(oldIndex); items.Insert(newIndex, movedItem); // 更新所有项的OrderIndex为当前索引 for (int i = 0; i < items.Count; i++) { items[i].OrderIndex = i; } // 更新ViewModel集合(引用变更触发Blazor变更检测) ViewModel.VerificationItems = items; StateHasChanged(); }
3. 让组件感知Items参数变化
修改BlazorSortable组件,在参数变更时重置Sortable,确保绑定最新DOM:
private DotNetObjectReference<BlazorSortable>? _dotNetRef; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { _dotNetRef = DotNetObjectReference.Create(this); await InitializeSortableInJavascript(); } } protected override async Task OnParametersSetAsync() { await base.OnParametersSetAsync(); // 非首次渲染时,若Items变更则重置Sortable if (!firstRender) { await JSRuntime.InvokeVoidAsync("destroySortable", _dropZoneContainer); await InitializeSortableInJavascript(); } } public async ValueTask DisposeAsync() { _dotNetRef?.Dispose(); await JSRuntime.InvokeVoidAsync("destroySortable", _dropZoneContainer); } public async Task InitializeSortableInJavascript() { await JSRuntime.InvokeVoidAsync("initializeSortable", _dropZoneContainer, _dotNetRef, Items, Options); }
4. 移除不必要的延迟
删除OnAfterRenderAsync中的Task.Delay(100),Blazor的OnAfterRenderAsync已保证DOM渲染完成,可直接初始化Sortable。
内容的提问来源于stack exchange,提问作者Vlado Pandžić
相关产品推荐
相关产品推荐

