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

Blazor待办应用最后一个复选框无法保持勾选且无法写入JSON文件

问题分析与解决方案

核心问题

  1. 双向绑定与手动事件冲突:复选框同时使用@bind="todo.IsDone"和@oninput="CheckboxChanged",导致状态更新逻辑冲突,最后一项的状态无法正确同步。
  2. TodoItem无属性变更通知:普通类TodoItem的属性变化无法被Blazor组件自动感知,引发UI与数据模型的状态不一致。
  3. 同步文件操作的不当包装:用Task.Run包裹同步文件写入方法,可能引发线程安全问题,且最后一项的状态未及时被捕获就执行了写入。

具体修复步骤

1. 移除冲突事件绑定,改用绑定回调

将复选框的@oninput替换为@bind:after,确保绑定完成后再执行写入逻辑,避免冲突:

<input @key="todo" type="checkbox" @bind="todo.IsDone" @bind:after="() => CheckboxChanged()" />

简化CheckboxChanged方法:

public async Task CheckboxChanged()
{
    await todoApi.WriteAsync(todos);
    // @bind:after会自动触发组件更新,无需手动调用StateHasChanged
}

2. 让TodoItem支持属性变更通知(推荐)

修改TodoItem实现INotifyPropertyChanged,确保属性变化时组件能及时感知:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class TodoItem : INotifyPropertyChanged
{
    private string _title;
    private bool _isDone;

    public string Title
    {
        get => _title;
        set
        {
            _title = value;
            OnPropertyChanged();
        }
    }

    public bool IsDone
    {
        get => _isDone;
        set
        {
            _isDone = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

3. 优化JSON读写为异步实现

将JsonApi的同步方法改为异步,避免阻塞线程:

using System.Text.Json;

namespace Todolist.API
{
    public class JsonApi: IApi
    {
        private readonly string _filePath = Path.Combine(AppContext.BaseDirectory, "todos.json");

        public async Task WriteAsync(List<TodoItem> todos)
        {
            string json = JsonSerializer.Serialize(todos);
            await File.WriteAllTextAsync(_filePath, json);
        }

        public async Task<List<TodoItem>> ReadAsync()
        {
            if (!File.Exists(_filePath))
            {
                await File.CreateAsync(_filePath);
                return new List<TodoItem>();
            }

            var json = await File.ReadAllTextAsync(_filePath);
            if (string.IsNullOrWhiteSpace(json))
                return new List<TodoItem>();

            return JsonSerializer.Deserialize<List<TodoItem>>(json) ?? new List<TodoItem>();
        }
    }

    // 更新IApi接口定义
    public interface IApi
    {
        Task WriteAsync(List<TodoItem> todos);
        Task<List<TodoItem>> ReadAsync();
    }
}

4. 调整组件初始化和添加逻辑

修改OnInitializedAsync和ValidFormSubmitted方法,适配异步接口:

protected override async Task OnInitializedAsync()
{
    todos = await todoApi.ReadAsync();
    Console.WriteLine("Initialized");
}

public async Task ValidFormSubmitted()
{
    Console.WriteLine("Clicked");

    if(!string.IsNullOrWhiteSpace(newTodo))
    {
        todos.Add(new TodoItem { Title = newTodo });
        await todoApi.WriteAsync(todos);
        newTodo = string.Empty;
    }
}

额外注意事项

  • 确保文件路径正确:使用AppContext.BaseDirectory获取应用根目录,避免因工作目录不同导致的文件读写问题。
  • 避免在UI线程执行同步IO操作:异步文件操作能提升Blazor应用的响应性,尤其是在Server模式下。

内容的提问来源于stack exchange,提问作者zigjag

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 19:41:07