You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 17:07:03