生成 Blazor 待办事项列表应用

注意

此版本不是本文的最新版本。 对于当前版本,请参阅此文的 ASP.NET Core 8.0 版本

本教程提供了生成和修改 Blazor 应用的基本工作经验。 有关更详细的 Blazor 指南,请参阅 Blazor 参考文档

了解如何:

  • 创建待办事项列表 Blazor 应用项目
  • 修改 Razor 组件
  • 在组件中使用事件处理和数据绑定
  • 在 Blazor 应用中使用路由

在本教程结束时,你将拥有一个正常运行的待办事项列表应用。

先决条件

下载并安装 .NET(如果尚且在系统上安装它,或者系统未安装最新版本)。

创建 Blazor 应用

在命令行界面中创建名为 TodoList 的新 Blazor Web 应用:

dotnet new blazor -o TodoList

-o|--output 选项为项目创建文件夹。 如果已为项目创建了文件夹,并在该文件夹中打开了命令行界面,那么请忽略用于创建项目的 -o|--output 选项和值。

使用以下任一托管模型在命令行界面中创建名为 Blazor 的新 TodoList 应用:

  • 要获得使用 Blazor Server 的体验,请使用以下命令创建应用:

    dotnet new blazorserver -o TodoList
    
  • 要获得使用 Blazor WebAssembly 的体验,请使用以下命令创建应用:

    dotnet new blazorwasm -o TodoList
    

上述命令将创建一个带有 -o|--output 选项的 TodoList 文件夹来保存应用。 TodoList 文件夹是项目的根文件夹。 使用以下命令将目录切换到 TodoList 文件夹:

cd TodoList

生成待办事项列表 Blazor 应用

使用以下命令向应用添加一个新的 TodoRazor 组件:

dotnet new razorcomponent -n Todo -o Components/Pages

上述命令中的 -n|--name 指定了新的 Razor 组件的名称。 新组件是在项目具有 -o|--output 选项的 Components/Pages 文件夹中创建的。

dotnet new razorcomponent -n Todo -o Pages

上述命令中的 -n|--name 指定了新的 Razor 组件的名称。 新组件是在项目具有 -o|--output 选项的 Pages 文件夹中创建的。

重要

Razor 组件文件名要求首字母大写。 打开 Pages 文件夹,确认 Todo 组件文件名以大写字母 T 开头。 文件名应为 Todo.razor

在任何文件编辑器中打开 Todo 组件,并在文件顶部进行以下更改:

  • 添加相对 URL 为 @page 的 Razor/todo 指令。
  • 在页面上实现交互性,使其不只是静态呈现。 交互式服务器呈现模式使组件能够处理来自服务器的 UI 事件。
  • 使用 PageTitle 组件添加页面标题,从而可以向页面添加 HTML <title> 元素。

在任何文件编辑器中打开 Todo 组件,并在文件顶部进行以下更改:

  • 添加相对 URL 为 @page 的 Razor/todo 指令。
  • 使用 PageTitle 组件添加页面标题,从而可以向页面添加 HTML <title> 元素。

在任何文件编辑器中打开 Todo 组件,然后添加相对 URL 为 /todo 的指令 @pageRazor。

Todo.razor

@page "/todo"
@rendermode InteractiveServer

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

@code {

}
@page "/todo"

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

@code {

}
@page "/todo"

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

@code {

}
@page "/todo"

<h3>Todo</h3>

@code {

}
@page "/todo"

<h3>Todo</h3>

@code {

}

保存 Todo.razor 文件。

Todo 组件添加到导航栏。

NavMenu 组件用于应用的布局。 布局是可避免应用中出现重复内容的组件。 当应用加载组件 URL 时,NavLink 组件会在应用的 UI 中提供提示。

NavMenu 组件的导航元素内容 (<nav>) 中,为 Todo 组件添加以下 <div> 元素。

Components/Layout/NavMenu.razor中:

Shared/NavMenu.razor中:

<div class="nav-item px-3">
    <NavLink class="nav-link" href="todo">
        <span class="oi oi-list-rich" aria-hidden="true"></span> Todo
    </NavLink>
</div>

保存 NavMenu.razor 文件。

TodoList 文件夹,在命令行界面中执行的 dotnet watch run 命令,以生成并运行应用。 应用运行后,请在应用的导航栏中选择 Todo 链接来访问新的 Todo 页面,该链接将在 /todo 处加载页面。

让应用继续运行命令行界面。 每次保存文件时,都将自动重新生成应用,并且会自动重载浏览器中的页面。

向项目的根目录添加 TodoItem.cs 文件(TodoList 文件夹),以保存一个用于表示待办项的类。 为 TodoItem 类使用以下 C# 代码。

TodoItem.cs

namespace BlazorSample;

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

注意

如果使用 Visual Studio 创建 TodoItem.cs 文件和 TodoItem 类,请使用以下方法之一:

  • 删除 Visual Studio 为类生成的命名空间。
  • 使用前面代码块中的“复制”按钮,并替换 Visual Studio 生成的文件的全部内容。

返回 Todo 组件并执行以下任务:

  • @code 块中为待办项添加一个字段。 Todo 组件使用此字段来维护待办项列表的状态。
  • 添加无序列表标记和 foreach 循环,以将每个待办项呈现为列表项 (<li>)。

Components/Pages/Todo.razor

@page "/todo"
@rendermode InteractiveServer

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

@code {
    private List<TodoItem> todos = new();
}

Pages/Todo.razor

@page "/todo"

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

@code {
    private List<TodoItem> todos = new();
}

Pages/Todo.razor

@page "/todo"

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

@code {
    private List<TodoItem> todos = new();
}

Pages/Todo.razor

@page "/todo"

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

@code {
    private List<TodoItem> todos = new();
}

Pages/Todo.razor

@page "/todo"

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

@code {
    private IList<TodoItem> todos = new List<TodoItem>();
}

该应用需要 UI 元素来将待办项添加到列表。 在未排序列表 (<ul>...</ul>) 下方添加一个文本输入 (<input>) 和一个按钮 (<button>):

@page "/todo"
@rendermode InteractiveServer

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" />
<button>Add todo</button>

@code {
    private List<TodoItem> todos = new();
}
@page "/todo"

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" />
<button>Add todo</button>

@code {
    private List<TodoItem> todos = new();
}
@page "/todo"

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" />
<button>Add todo</button>

@code {
    private List<TodoItem> todos = new();
}
@page "/todo"

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" />
<button>Add todo</button>

@code {
    private List<TodoItem> todos = new();
}
@page "/todo"

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" />
<button>Add todo</button>

@code {
    private IList<TodoItem> todos = new List<TodoItem>();
}

保存 TodoItem.cs 文件和更新的 Todo.razor 文件。 在命令行界面中,保存文件时,将自动重新生成应用。 浏览器重载页面。

选择“Add todo”按钮时没有任何反应,因为没有事件处理程序连接到该按钮。

Todo 组件添加 AddTodo 方法,并使用 @onclick 属性来为按钮注册方发。 选择按钮时,会调用 AddTodo C# 方法:

<input placeholder="Something todo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private List<TodoItem> todos = new();

    private void AddTodo()
    {
        // Todo: Add the todo
    }
}
<input placeholder="Something todo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private List<TodoItem> todos = new();

    private void AddTodo()
    {
        // Todo: Add the todo
    }
}
<input placeholder="Something todo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private List<TodoItem> todos = new();

    private void AddTodo()
    {
        // Todo: Add the todo
    }
}
<input placeholder="Something todo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private List<TodoItem> todos = new();

    private void AddTodo()
    {
        // Todo: Add the todo
    }
}
<input placeholder="Something todo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private IList<TodoItem> todos = new List<TodoItem>();

    private void AddTodo()
    {
        // Todo: Add the todo
    }
}

若要获取新的待办项的标题,请在 @code 块的顶部添加一个 newTodo 字符串:

private string? newTodo;
private string newTodo;

修改文本 <input> 元素,使用 @bind 属性来绑定 newTodo

<input placeholder="Something todo" @bind="newTodo" />

更新 AddTodo 方法,将具有指定标题的 TodoItem 添加到列表。 通过将 newTodo 设置为空字符串来清除文本输入的值:

@page "/todo"
@rendermode InteractiveServer

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

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

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
@page "/todo"

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

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

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
@page "/todo"

<PageTitle>Todo</PageTitle>

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

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

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
@page "/todo"

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private List<TodoItem> todos = new();
    private string newTodo;

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
@page "/todo"

<h3>Todo</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Title</li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private IList<TodoItem> todos = new List<TodoItem>();
    private string newTodo;

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}

保存 Todo.razor 文件。 应用会在命令行界面中自动重新生成,并且页面在浏览器中重载。

每个 Todo 项的标题文本都可编辑,复选框可帮助用户跟踪已完成的项。 为每个 Todo 项添加一个复选框输入,并将它的值绑定到 IsDone 属性。 将 @todo.Title 更改为 <input> 元素,后者通过 @bind 绑定到 todo.Title

<ul>
      @foreach (var todo in todos)
      {
         <li>
            <input type="checkbox" @bind="todo.IsDone" />
            <input @bind="todo.Title" />
         </li>
      }
</ul>

更新 <h3> 标头,显示尚未完成的待办项数目(IsDonefalse)。 每次 Blazor 重新呈现组件时,都会计算以下标头中的 Razor 表达式。

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

已完成的 Todo 组件:

@page "/todo"
@rendermode InteractiveServer

<PageTitle>Todo</PageTitle>

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

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="todo.IsDone" />
            <input @bind="todo.Title" />
        </li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

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

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
@page "/todo"

<PageTitle>Todo</PageTitle>

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

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="todo.IsDone" />
            <input @bind="todo.Title" />
        </li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

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

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
@page "/todo"

<PageTitle>Todo</PageTitle>

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

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="todo.IsDone" />
            <input @bind="todo.Title" />
        </li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

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

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
@page "/todo"

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

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="todo.IsDone" />
            <input @bind="todo.Title" />
        </li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

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

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
@page "/todo"

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

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="todo.IsDone" />
            <input @bind="todo.Title" />
        </li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

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

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}

保存 Todo.razor 文件。 应用会在命令行界面中自动重新生成,并且页面在浏览器中重载。

添加项、编辑项,并将待办标记为“已完成”来测试组件。

完成后,请在命令行界面中关闭应用。 许多命令行界面接受键盘命令 Ctrl+C (Windows) 或 +C (macOS) 来停止应用。

发布到 Azure

有关部署到 Azure 的信息,请参阅快速入门:部署 ASP.NET Web 应用

后续步骤

在本教程中,你将了解:

  • 创建待办事项列表 Blazor 应用项目
  • 修改 Razor 组件
  • 在组件中使用事件处理和数据绑定
  • 在 Blazor 应用中使用路由

了解用于 ASP.NET Core Blazor 的工具: