我在 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;
}
}
}
}
您试图通过将 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);
}
}