Blazor向Dictionary<DateTime,Item>插入行后渲染异常求助
问题
我需要创建一个包含自定义组件的动态表格,支持在表格首行插入新行。编写了以下组件:
Page 组件
@page "/test" <h3>Test</h3> <table class="table table-striped"> <thead> <tr> <th scope="col">Id</th> <th scope="col">Date</th> <th scope="col">Score</th> <th scope="col">Name</th> </tr> </thead> <tbody> @foreach (Item item in items.Values.OrderByDescending(i => i.date)) { <tr> <td>@item.Id</td> <td>@item.date</td> <td> <TestComponent OnValueChanged="(v)=>{ item.Score = Convert.ToInt32(v); }" Id="@(item.Id.ToString())" Item="item.Score" /> </td> <td> <TestStringComponent Id="@(item.Id.ToString())" @bind-Item="item.Name" /> </td> </tr> } </tbody> </table> <button class="btn" @onclick="Add">Add</button> @code { public class Item { public string Id { get; set; } public DateTime date { get; set; } public int? Score { get; set; } public string? Name { get; set; } public bool ShowEdit{ get; set; } } public Dictionary<DateTime, Item> items = new Dictionary<DateTime, Item>(); public void Add() { DateTime nextDate = items.Keys.Max().AddDays(1); items.Add(nextDate, new Item { Id = "ID "+(items.Count + 1), date = nextDate, Score = items.Count + 1, Name = "Z"}); } protected override void OnInitialized() { base.OnInitialized(); items.Add(new DateTime(2021, 1, 1), new Item { Id = "ID "+1, Score = null, Name = "A", date = new DateTime(2021, 1, 1) }); items.Add(new DateTime(2021, 1, 2), new Item { Id = "ID " + 2, Score = 2, Name = "B", date = new DateTime(2021, 1, 2) }); items.Add(new DateTime(2021, 1, 3), new Item { Id = "ID " + 3, Score = null, Name = "C", date = new DateTime(2021, 1, 3) }); items.Add(new DateTime(2021, 1, 4), new Item { Id = "ID " + 4, Score = 4, Name = "D", date = new DateTime(2021, 1, 4) }); items.Add(new DateTime(2021, 1, 5), new Item { Id = "ID " + 5, Score = null, Name = "E", date = new DateTime(2021, 1, 5) }); } }
TestStringComponent
@inject IJSRuntime JS @Item @code { public String? _Item { get; set; } [Parameter] public String? Item { get { return _Item; } set { if (value != _Item) { if(_Item != null || value != null) { Console.WriteLine(Id+": "+_Item +"->"+ value); } _Item = value; if (ItemChanged.HasDelegate) { ItemChanged.InvokeAsync(_Item); } } } } [Parameter] public string Id { get; set; } [Parameter] public EventCallback<String?> ItemChanged { get; set; } }
TestComponent
@inject IJSRuntime JS @Item @code { public double? _Item { get; set; } [Parameter] public double? Item { get { return _Item; } set { if (value != _Item) { if(_Item != null || value != null) { Console.WriteLine(Id+": "+_Item +"->"+ value); } _Item = value; if (OnValueChanged.HasDelegate) { OnValueChanged.InvokeAsync(_Item); } } } } [Parameter] public string Id { get; set; } [Parameter] public EventCallback<double?> OnValueChanged { get; set; } }
首次渲染正常,但点击Add按钮插入首行后出现异常:部分空Score被填充为0,Name被替换为"Z",且所有单元格的set事件均被触发。由于使用Entity Framework追踪实体变更,这可能导致数据库数据污染。需要实现插入首行时不触发set事件,且不替换其他单元格的值。
解决方案
1. 修复类型不匹配问题
Item.Score是int?类型,但TestComponent的Item参数定义为double?,null的int?传递时会触发隐式转换问题,导致空值被转为0。修改TestComponent的类型为int?:
@inject IJSRuntime JS @Item @code { public int? _Item { get; set; } [Parameter] public int? Item { get => _Item; set { if (value != _Item) { if(_Item != null || value != null) { Console.WriteLine(Id+": "+_Item +"->"+ value); } _Item = value; if (OnValueChanged.HasDelegate) { OnValueChanged.InvokeAsync(_Item); } } } } [Parameter] public string Id { get; set; } [Parameter] public EventCallback<int?> OnValueChanged { get; set; } }
同时简化Page组件中的回调,无需强制转换:
<TestComponent OnValueChanged="(v)=>{ item.Score = v; }" Id="@item.Id" Item="item.Score" />
2. 添加@key避免组件复用
Blazor的foreach循环默认会复用组件实例,插入新行导致顺序变化时,会重新绑定现有组件参数触发setter。给循环的<tr>添加唯一@key,确保每个Item对应独立组件:
@foreach (Item item in items.Values.OrderByDescending(i => i.date)) { <tr @key="item.Id"> <!-- 原有内容 --> </tr> }
3. 优化组件参数处理逻辑
不要在参数setter中修改源数据或触发回调,改用生命周期方法处理参数变化:
// TestStringComponent示例 @inject IJSRuntime JS @Item @code { private string? _internalItem; [Parameter] public string? Item { get; set; } [Parameter] public EventCallback<string?> ItemChanged { get; set; } protected override void OnParametersSet() { if (Item != _internalItem) { if(_internalItem != null || Item != null) { Console.WriteLine(Id+": "+_internalItem +"->"+ Item); } _internalItem = Item; } } }
双向绑定场景下,Blazor会自动处理ItemChanged回调,无需手动触发。
4. 修复Add方法的Id计算错误
当前Id = "ID "+(items.Count + 1)会导致Id重复,提前计算新Id:
public void Add() { DateTime nextDate = items.Keys.Max().AddDays(1); int newIdNum = items.Count + 1; items.Add(nextDate, new Item { Id = $"ID {newIdNum}", date = nextDate, Score = newIdNum, Name = "Z"}); }
内容的提问来源于stack exchange,提问作者Jian Li
相关产品推荐
相关产品推荐

