diff --git a/src/BootstrapBlazor/Components/Table/Table.razor b/src/BootstrapBlazor/Components/Table/Table.razor index d69fc591142..0dcb3fcb52b 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor +++ b/src/BootstrapBlazor/Components/Table/Table.razor @@ -282,13 +282,15 @@ { if (item is IDynamicObject d) { - } else { - } diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index da165529237..b644c187b4a 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Components.Web.Virtualization; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; +using System.Collections.Concurrent; using System.ComponentModel.DataAnnotations; using System.Data; using System.Reflection; @@ -6474,6 +6475,126 @@ public async Task DynamicContext_EqualityComparer() Assert.True(compared); } + [Fact] + public async Task DynamicContext_CardView_CheckGuid_Ok() + { + var localizer = Context.Services.GetRequiredService>(); + var selectedRows = new List(); + var cut = Context.Render(pb => + { + pb.AddChildContent>(pb => + { + pb.Add(a => a.RenderMode, TableRenderMode.CardView); + pb.Add(a => a.IsMultipleSelect, true); + pb.Add(a => a.DynamicContext, CreateDynamicContext(localizer)); + pb.Add(a => a.SelectedRows, selectedRows); + pb.Add(a => a.SelectedRowsChanged, EventCallback.Factory.Create>(this, rows => selectedRows = rows)); + }); + }); + + // CardView 模式无表头 + // 共 2 行数据选中第一行数据 + var input = cut.FindComponents>()[0]; + await cut.InvokeAsync(input.Instance.OnToggleClick); + + Assert.Single(selectedRows); + Assert.Equal(0, selectedRows[0].GetValue("Id")); + + // 取消选中 + await cut.InvokeAsync(input.Instance.OnToggleClick); + Assert.Empty(selectedRows); + } + + [Fact] + public async Task DynamicContext_HeaderCheckGuid_Ok() + { + var localizer = Context.Services.GetRequiredService>(); + var selectedRows = new List(); + var cut = Context.Render(pb => + { + pb.AddChildContent>(pb => + { + pb.Add(a => a.RenderMode, TableRenderMode.Table); + pb.Add(a => a.IsMultipleSelect, true); + pb.Add(a => a.DynamicContext, CreateDynamicContext(localizer)); + pb.Add(a => a.SelectedRows, selectedRows); + pb.Add(a => a.SelectedRowsChanged, EventCallback.Factory.Create>(this, rows => selectedRows = rows)); + }); + }); + + // 无选中行 + Assert.Empty(selectedRows); + + // 点击表头全选,选中行为 2 行 + var header = cut.FindComponents>()[0]; + await cut.InvokeAsync(header.Instance.OnToggleClick); + Assert.Equal(2, selectedRows.Count); + + // 再次点击表头全选,取消全选 + await cut.InvokeAsync(header.Instance.OnToggleClick); + Assert.Empty(selectedRows); + } + + [Fact] + public async Task DynamicContext_HeaderCheckGuid_ShowRowCheckboxCallback_Ok() + { + // 测试包含无法选中行逻辑 + var localizer = Context.Services.GetRequiredService>(); + var selectedRows = new List(); + var cut = Context.Render(pb => + { + pb.AddChildContent>(pb => + { + pb.Add(a => a.RenderMode, TableRenderMode.Table); + pb.Add(a => a.IsMultipleSelect, true); + pb.Add(a => a.DynamicContext, CreateDynamicContext(localizer)); + pb.Add(a => a.ShowRowCheckboxCallback, row => Equals(row.GetValue("Id"), 0)); + pb.Add(a => a.SelectedRows, selectedRows); + pb.Add(a => a.SelectedRowsChanged, EventCallback.Factory.Create>(this, rows => selectedRows = rows)); + }); + }); + + Assert.Equal(2, cut.FindComponents>().Count); + + var header = cut.FindComponents>()[0]; + await cut.InvokeAsync(header.Instance.OnToggleClick); + Assert.Single(selectedRows); + } + + + [Fact] + public async Task DynamicContext_ChangeDetection_Ok() + { + var localizer = Context.Services.GetRequiredService>(); + var cut = Context.Render(pb => + { + pb.AddChildContent>(pb => + { + pb.Add(a => a.RenderMode, TableRenderMode.Table); + pb.Add(a => a.IsMultipleSelect, true); + pb.Add(a => a.DynamicContext, CreateDynamicContext(localizer)); + }); + }); + + cut.Dispose(); + + // 表格使用动态创建类型后,不能被 Blazor 底层 ChangeDetection 缓存,否则生成的动态 Assembly 无法被释放 + // 通过反射查看是否被缓存 + var type = typeof(ComponentBase).Assembly.GetType("Microsoft.AspNetCore.Components.ChangeDetection"); + Assert.NotNull(type); + + var fieldInfo = type.GetField("_immutableObjectTypesCache", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + Assert.NotNull(fieldInfo); + + IEnumerable? items = null; + if (fieldInfo.GetValue(null) is ConcurrentDictionary cache) + { + items = cache.Keys.Where(i => i.Assembly.GetName().Name == "BootstrapBlazor_DynamicAssembly"); + } + Assert.NotNull(items); + Assert.Empty(items); + } + [Fact] public async Task DynamicContext_Add() {