生成应用

生成首个 Blazor 应用

作者:Daniel RothLuke Latham

重要

Blazor WebAssembly 为预览版状态

ASP.NET Core 3.0 支持 Blazor Server 。 Blazor WebAssembly 在 ASP.NET Core 3.1 中为预览版。

本教程演示如何生成和修改 Blazor 应用。

按照 ASP.NET Core Blazor 入门 文章中的指南创建用于本教程的 Blazor 项目。 将项目命名为 ToDoList 。

生成组件

  1. 在 Pages 文件夹中浏览应用的三个页面:主页、计数器和提取数据。 这些页面由 Razor 组件文件(Index.razor 、Counter.razor 和 FetchData.razor )实现。

  2. 在“计数器”页上,选择“单击我” 按钮,在不刷新页面的情况下增加计数器值。 增加网页的计数器值通常需要编写 JavaScript。 通过 Blazor,可以改为编写 C#。

  3. 检查 Counter.razor 文件中 Counter 组件的实现。

    Pages/Counter.razor

    @page "/counter"
    
    <h1>Counter</h1>
    
    <p>Current count: @currentCount</p>
    
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
    
    @code {
        private int currentCount = 0;
    
        private void IncrementCount()
        {
            currentCount++;
        }
    }
    

    使用 HTML 定义 Counter组件的 UI。 动态呈现逻辑(例如,循环、条件、表达式)是使用名为 Razor 的嵌入式 C# 语法添加的。 HTML 标记和 C# 呈现逻辑在构建时转换为组件类。 生成的 .NET 类的名称与文件名匹配。

    组件类的成员在 @code 块中定义。 @code 块中,可以指定组件状态(属性、字段)和方法用于处理事件或定义其他组件逻辑。 然后,可以将这些成员用作组件呈现逻辑的一部分,并用于处理事件。

    选中“单击我” 按钮时:

    • 调用 Counter 组件的已注册 onclick 处理程序(IncrementCount 方法)。
    • Counter 组件重新生成其呈现树。
    • 将新的呈现树与前一个呈现树进行比较。
    • 仅应用对文档对象模型 (DOM) 的修改。 显示的计数将会更新。
  4. 修改 Counter 组件的 C# 逻辑,使计数递增 2 而不是 1。

    @page "/counter"
    
    <h1>Counter</h1>
    
    <p>Current count: @currentCount</p>
    
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
    
    @code {
        private int currentCount = 0;
    
        private void IncrementCount()
        {
            currentCount += 2;
        }
    }
    
  5. 重新生成并运行应用以查看更改。 选择“单击我” 按钮。 计数器的值将增加 2。

使用组件

使用 HTML 语法将组件加入到另一个组件中。

  1. 通过向 Index 组件 (Index.razor ) 添加 <Counter /> 元素,将 Counter 组件添加到应用的 Index 组件。

    如果在此体验中使用的是 Blazor WebAssembly,则 Index 组件使用 SurveyPrompt 组件。 <SurveyPrompt> 元素替换为 <Counter /> 元素。 如果在此体验中使用的是 Blazor Server 应用,请向 Index 组件添加 <Counter /> 元素:

    Pages/Index.razor

    @page "/"
    
    <h1>Hello, world!</h1>
    
    Welcome to your new app.
    
    <Counter />
    
  2. 重新生成并运行应用。 Index 组件有其自己的计数器。

组件参数

组件也可以有参数。 组件参数由具有 [Parameter] 的组件类上的公共属性定义。 使用这些属性在标记中为组件指定参数。

  1. 更新组件的 @code C# 代码,如下所示:

    • 使用 [Parameter] 特性添加公共 IncrementAmount 属性。
    • 增加 currentCount 的值时,更改 IncrementCount 方法以使用 IncrementAmount 属性。

    Pages/Counter.razor

    @page "/counter"
    
    <h1>Counter</h1>
    
    <p>Current count: @currentCount</p>
    
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
    
    @code {
        private int currentCount = 0;
    
        [Parameter]
        public int IncrementAmount { get; set; } = 1;
    
        private void IncrementCount()
        {
            currentCount += IncrementAmount;
        }
    }
    
  2. 使用属性在 Index 组件的 <Counter> 元素中指定 IncrementAmount 参数。 将计数器递增值设置为 10。

    Pages/Index.razor

    @page "/"
    
    <h1>Hello, world!</h1>
    
    Welcome to your new app.
    
    <Counter IncrementAmount="10" />
    
  3. 重新加载 Index 组件。 每次选择“单击我” 按钮时,计数器值递增 10。 Counter 组件中的计数器继续递增 1。

路由到组件

Counter.razor 文件顶部的 @page 指令指定 Counter 组件是路由终结点。 Counter 组件处理发送到 /counter 的请求。 如果没有 @page 指令,组件将无法处理路由的请求,但该组件仍可以被其他组件使用。

依赖关系注入

Blazor Server 体验

如果使用的是 Blazor Server 应用,则 WeatherForecastService 服务在 Startup.ConfigureServices 中注册为单一实例 可通过依赖关系注入 (DI) 在整个应用中使用服务的实例:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton<WeatherForecastService>();
}

@inject 指令用于将 WeatherForecastService 服务的实例注入到 FetchData 组件中。

Pages/FetchData.razor

@page "/fetchdata"
@using ToDoList.Data
@inject WeatherForecastService ForecastService

FetchData 组件使用注入的服务(作为 ForecastService)来检索 WeatherForecast 对象的数组:

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

Blazor WebAssembly 体验

如果使用的是 Blazor WebAssembly 应用,则注入了 HttpClient,以从 wwwroot/sample-data 文件夹的 weather.json 文件中获取天气预测数据。

Pages/FetchData.razor

@inject HttpClient Http

...

protected override async Task OnInitializedAsync()
{
    forecasts = 
        await Http.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json");
}

@foreach 循环用于将每个预测实例呈现为“天气”数据表中的一行:

<table class="table">
    <thead>
        <tr>
            <th>Date</th>
            <th>Temp. (C)</th>
            <th>Temp. (F)</th>
            <th>Summary</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var forecast in forecasts)
        {
            <tr>
                <td>@forecast.Date.ToShortDateString()</td>
                <td>@forecast.TemperatureC</td>
                <td>@forecast.TemperatureF</td>
                <td>@forecast.Summary</td>
            </tr>
        }
    </tbody>
</table>

生成待办项列表

向应用添加一个实现简单待办事项列表的新组件。

  1. 向 Pages 文件夹中的应用添加一个名为 Todo.razor 的空文件:

  2. 为组件提供初始标记:

    @page "/todo"
    
    <h1>Todo</h1>
    
  3. Todo 组件添加到导航栏。

    NavMenu 组件 (Shared/NavMenu.razor ) 用于应用的布局。 布局是可以避免应用中出现重复内容的组件。

    通过在“Shared/NavMenu.razor” 文件中的现有列表项下添加以下列表项标记,为 Todo 组件添加一个 <NavLink> 元素:

    <li class="nav-item px-3">
        <NavLink class="nav-link" href="todo">
            <span class="oi oi-list-rich" aria-hidden="true"></span> Todo
        </NavLink>
    </li>
    
  4. 重新生成并运行应用。 访问新的“待办事项”页面,确认指向 Todo 组件的链接有效。

  5. 向项目的根目录添加“TodoItem.cs” 文件,以保存一个用于表示待办项的类。 TodoItem 类使用以下 C# 代码:

    public class TodoItem
    {
        public string Title { get; set; }
        public bool IsDone { get; set; }
    }
    
  6. 返回到 Todo 组件 (Pages/Todo.razor ):

    • @code 块中为待办项添加一个字段。 Todo 组件使用此字段来维护待办项列表的状态。
    • 添加无序列表标记和 foreach 循环,以将每个待办项呈现为列表项 (<li>)。
    @page "/todo"
    
    <h1>Todo</h1>
    
    <ul>
        @foreach (var todo in todos)
        {
            <li>@todo.Title</li>
        }
    </ul>
    
    @code {
        private IList<TodoItem> todos = new List<TodoItem>();
    }
    
  7. 该应用需要 UI 元素来将待办项添加到列表。 在未排序列表 (<ul>...</ul>) 下方添加一个文本输入 (<input>) 和一个按钮 (<button>):

    @page "/todo"
    
    <h1>Todo</h1>
    
    <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>();
    }
    
  8. 重新生成并运行应用。 选择“添加待办项” 按钮时没有任何反应,因为没有事件处理程序连接到该按钮。

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

    <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
        }
    }
    
  10. 要获得新待办项标题,请在 @code 块顶部添加 newTodo 字符串字段,并使用 <input> 元素中的 bind 属性将其绑定到文本输入的值:

    private IList<TodoItem> todos = new List<TodoItem>();
    private string newTodo;
    
    <input placeholder="Something todo" @bind="newTodo" />
    
  11. 更新 AddTodo 方法,将具有指定标题的 TodoItem 添加到列表。 通过将 newTodo 设置为空字符串来清除文本输入的值:

    @page "/todo"
    
    <h1>Todo</h1>
    
    <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;
            }
        }
    }
    
  12. 重新生成并运行应用。 在待办项列表中添加一些待办项以测试新代码。

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

    <ul>
        @foreach (var todo in todos)
        {
            <li>
                <input type="checkbox" @bind="todo.IsDone" />
                <input @bind="todo.Title" />
            </li>
        }
    </ul>
    
  14. 若要验证这些值是否已绑定,请更新 <h1> 标头以显示尚未完成的待办项计数(IsDonefalse)。

    <h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>
    
  15. 完成的 Todo 组件 (Pages/Todo.razor ):

    @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 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;
            }
        }
    }
    
  16. 重新生成并运行应用。 添加待办项以测试新代码。