在 Blazor 中复选框不会保持选中状态

问题描述 投票:0回答:1

我在 Blazor 中有一个简单的待办事项列表应用程序,当我检查待办事项时,待办事项列表将写入 json 文件。它一直工作到最后一个复选框。最后一个复选框不会写入 json 文件,也不会保持选中状态。

我尝试写入文本文件以及调用异步方法和 StateHasChanged,但似乎没有任何效果。

@page "/todo"
@rendermode InteractiveServer
@inject API.IApi todoApi

<PageTitle>Todo</PageTitle>

<h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="ValidFormSubmitted" class="btn btn-primary">Add Todo</button>

<ul class="list-unstyled">
    @foreach (var todo in todos)
    {
        <li>
            <input @key="todo" type="checkbox" @bind="todo.IsDone" @oninput="CheckboxChanged"/>
            <input class="border-0" type="text" @bind="todo.Title" />
        </li>
    }
</ul>

@code {
    public List<TodoItem> todos = new();
    public string? newTodo = "";

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

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

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

    public async Task CheckboxChanged(EventArgs e)
    {
        await Task.Run(() => todoApi.Write(todos));
        await InvokeAsync(StateHasChanged);
    }

}

以下是写入 json 文件的代码:

using System.Text.Json;

namespace Todolist.API
{
    public class JsonApi: IApi
    {

        public void Write(List<TodoItem> todos)
        {
            string json = JsonSerializer.Serialize(todos);
            File.WriteAllText("todos.json", json);
        }

        public List<TodoItem> Read()
        {
            string filename = "todos.json";
            string json = "";
            List<TodoItem> todos = new List<TodoItem>();

            if (File.Exists(filename) && File.ReadLines(filename).Any())
            {
                json = File.ReadAllText(filename);
                todos = JsonSerializer.Deserialize<List<TodoItem>>(json);
                return todos;
            }
            else
            {
                File.Create(filename).Close();
                return todos;
            }
        }
    }
}

最后一个复选框未选中的屏幕截图

c# blazor
1个回答
0
投票

您试图通过将 I/O 卸载到另一个线程来使其异步。 你不需要。 使用异步版本的读取和写入。

我不会试图解释原因,而是向您指出这篇文章,其中解释了异步 I/O 操作和没有线程真相 - https://blog.stephencleary.com/2013/11/there -is-no-thread.html.

这是代码的重构版本:


public class TodoItem
{
    public string? Title { get; set; }
    public bool IsDone { get; set; }
}

public class JsonApi
{
    public async ValueTask WriteAsync(List<TodoItem> todos)
    {
        string json = JsonSerializer.Serialize(todos);
        await File.WriteAllTextAsync("todos.json", json);
    }

    public async ValueTask<List<TodoItem>> ReadAsync()
    {
        string filename = "todos.json";
        string json = "";
        List<TodoItem> todos = new List<TodoItem>();

        if (File.Exists(filename) && File.ReadLines(filename).Any())
        {
            json = await File.ReadAllTextAsync(filename);
            todos = JsonSerializer.Deserialize<List<TodoItem>>(json) ?? new List<TodoItem>();
            return todos;
        }
        else
        {
            File.Create(filename).Close();
            return todos;
        }
    }
}

在组件代码中,当您应该使用

@oninput
时,您却使用了
@bind:after
。这保证了在您尝试保存模型之前该值将被更新。

@page "/"
@inject JsonApi todoApi

<PageTitle>Home</PageTitle>

<PageTitle>Todo</PageTitle>

<h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="ValidFormSubmitted" class="btn btn-primary">Add Todo</button>

<ul class="list-unstyled">
    @foreach (var todo in todos)
    {
        <li>
            <input @key="todo" type="checkbox" @bind="todo.IsDone" @bind:after="CheckboxChanged" />
            <input class="border-0" type="text" @bind="todo.Title" />
        </li>
    }
</ul>

<div class="bg-dark text-white m-2 p-2">
    @foreach(var item in todos)
    {
        <pre>@item.Title - @item.IsDone</pre>
    }
</div>

@code {
    public List<TodoItem> todos = new();
    public string? newTodo = "";

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

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

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

    public async Task CheckboxChanged()
    {
        await todoApi.WriteAsync(todos);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.