隐藏

使用Blazor构建web应用程序 .NET 6篇 中

发布:2022/1/13 14:36:44作者:管理员 来源:本站 浏览次数:859

   使用Blazor构建web应用程序

       第五节Exercise - Access data from a Blazor component (练习-从Blazor组件访问数据)

       第六节 Share data in Blazor applications(在Blazor应用程序中共享数据)

       第七节Exercise - Share data in Blazor applications(练习-在Blazor应用程序中共享数据)



使用Blazor构建web应用程序 .NET 6篇 中)


使用Blazor构建web应用程序


Build web applications with Blazor微软学习文档尚未更新中文,为方便小伙伴们共同学习,博主翻译了教程,并修改了原文中个别代码错误,以下教程翻译属个人行为,如有侵权,请联系博主BlazorComponent,博主会在第一时间删除教程!

上一篇

使用Blazor构建web应用程序 .NET 6篇 上

QQ交流群:740905824

第五节Exercise - Access data from a Blazor component (练习-从Blazor组件访问数据)


The current hard-coded pizzas in the app need to be replaced with a database. The Microsoft Entity Framework allows you to add connections to data sources. In our app, we'll use a SQLite database to store our pizzas.

当前应用程序中硬编码的pizzas数据需要替换为数据库数据。Microsoft Entity Framework允许你向数据源添加连接。在我们的应用程序中,我们将使用SQLite数据库来存储pizzas。

In this exercise, you'll add packages to support our database functionality, connect our classes to a backend database, and add a helper class to pre-load data for the companies pizzas.

在此练习中我们将添加程序包以支持数据库功能,把类连接到后端数据库,并添加帮助器类以预加载公司pizzas的数据。


Add packages to support database access

添加支持数据库访问的包


1.In Visual Studio Code, select the Terminal menu, then select New Terminal.

在Visual Studio Code中,选择“终端”菜单,然后选择“新建终端”。

2.Run these commands to add the Microsoft.EntityFrameworkCore ,Microsoft.EntityFrameworkCore.Sqlite, and System.Net.Http.Json packages.

运行这些命令以添加Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.Sqlite和System.Net.Http.Json包。


   PowerShell


dotnet add package Microsoft.EntityFrameworkCore

dotnet add package Microsoft.EntityFrameworkCore.Sqlite

dotnet add package System.Net.Http.Json


 


These commands add package references to your BlazingPizza.csproj file.

这些命令将包引用添加到BlazingPizza.csproj文件。


   XML


<ItemGroup>

   <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />

   <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" />

   <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />

</ItemGroup>


  


Add a database context and controller

添加数据库上下文和控制器


1.In Visual Studio Code, select the File menu, then New File.

在Visual Studio Code中,选择“文件”菜单,然后选择“新建文件”。

2.For the language, select C#.

选择C#语言。

3.Enter this code for the class.

为类输入此代码。


   C#


using Microsoft.EntityFrameworkCore;


namespace BlazingPizza

{

 public class PizzaStoreContext : DbContext

 {

   public PizzaStoreContext(DbContextOptions options) : base(options)

   {

   }


   public DbSet<PizzaSpecial> Specials { get; set; }

 }

}


 


This class creates a database context we can use to register a database service. The context will also allow us to have a controller that will access the database.

这个类创建了一个数据库上下文,我们可以用它来注册数据库服务。上下文还允许我们拥有一个访问数据库的控制器。


4.Press CTRL+S, then in the Save As dialog, for File name enter PizzaStoreContext.cs, and then select Save.

按CTRL+S,然后在“另存为”对话框中,输入PizzaStoreContext.cs作为“文件名”,然后选择“保存”。

5.Select the File menu, then New File.

选择“文件”菜单,然后选择“新建文件”。

6.For the language, select C#.

选择C#语言。

7.Enter this code for the class.

为类输入此代码。


   C#


using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using Microsoft.AspNetCore.Mvc;

using Microsoft.EntityFrameworkCore;


namespace BlazingPizza

{

   [Route("specials")]

   [ApiController]

   public class SpecialsController : Controller

   {

       private readonly PizzaStoreContext _db;


       public SpecialsController(PizzaStoreContext db)

       {

           _db = db;

       }


       [HttpGet]

       public async Task<ActionResult<List<PizzaSpecial>>> GetSpecials()

       {

           return (await _db.Specials.ToListAsync()).OrderByDescending(s => s.BasePrice).ToList();

       }

   }

}


 


This class creates a controller that will allow us to query the database for pizza specials and return them as JSON at the http://localhost:5000/specials url.

这个类创建了一个控制器,允许我们查询数据库中的Specials,并在http://localhost:5000/specials 中以JSON形式返回Specials。


8.Press CTRL+S. In the Save As dialog, for File name enter SpecialsController.cs, and then select Save.

按CTRL+S。在“另存为”对话框中,输入SpecialsController.cs作为“文件名”,然后选择“保存”。


Load data into the database

将数据加载到数据库中


The app will check to see if there's an existing SQLite database, and create one with some pre-made pizzas.

该应用程序将检查是否存在现有的SQLite数据库,并使用一些预先制作的pizzas种子数据创建一个数据库。

1.Select the File menu, then New File.

选择“文件”菜单,然后选择“新建文件”。

2.For the language, select C#.

选择C#语言。

3.Enter this code for the class.

为类输入此代码。


   C#


namespace BlazingPizza

{

   public static class SeedData

   {

       public static void Initialize(PizzaStoreContext db)

       {

           var specials = new PizzaSpecial[]

           {

               new PizzaSpecial()

               {

                   Name = "Basic Cheese Pizza",

                   Description = "It's cheesy and delicious. Why wouldn't you want one?",

                   BasePrice = 9.99m,

                   ImageUrl = "img/pizzas/cheese.jpg",

               },

               new PizzaSpecial()

               {

                   Id = 2,

                   Name = "The Baconatorizor",

                   Description = "It has EVERY kind of bacon",

                   BasePrice = 11.99m,

                   ImageUrl = "img/pizzas/bacon.jpg",

               },

               new PizzaSpecial()

               {

                   Id = 3,

                   Name = "Classic pepperoni",

                   Description = "It's the pizza you grew up with, but Blazing hot!",

                   BasePrice = 10.50m,

                   ImageUrl = "img/pizzas/pepperoni.jpg",

               },

               new PizzaSpecial()

               {

                   Id = 4,

                   Name = "Buffalo chicken",

                   Description = "Spicy chicken, hot sauce and bleu cheese, guaranteed to warm you up",

                   BasePrice = 12.75m,

                   ImageUrl = "img/pizzas/meaty.jpg",

               },

               new PizzaSpecial()

               {

                   Id = 5,

                   Name = "Mushroom Lovers",

                   Description = "It has mushrooms. Isn't that obvious?",

                   BasePrice = 11.00m,

                   ImageUrl = "img/pizzas/mushroom.jpg",

               },

               new PizzaSpecial()

               {

                   Id = 7,

                   Name = "Veggie Delight",

                   Description = "It's like salad, but on a pizza",

                   BasePrice = 11.50m,

                   ImageUrl = "img/pizzas/salad.jpg",

               },

               new PizzaSpecial()

               {

                   Id = 8,

                   Name = "Margherita",

                   Description = "Traditional Italian pizza with tomatoes and basil",

                   BasePrice = 9.99m,

                   ImageUrl = "img/pizzas/margherita.jpg",

               },

           };

           db.Specials.AddRange(specials);

           db.SaveChanges();

       }

   }

}


  


The class uses a passed database context, creates some PizzaSpecial objects in an array, and then saves them.

该类使用传递的数据库上下文,在数组中创建一些PizzaSpecial对象,然后保存它们。


4.Press CTRL+S. In the Save As dialog, for File name enter SeedData.cs, then select Save.

按CTRL+S。在“另存为”对话框中,输入SeedData.cs作为“文件名”,然后选择“保存”。

5.In the explorer, select Program.cs.

在资源管理器中,选择Program.cs。

6.At the top, add a reference to a new package.

在顶部,添加对新包的引用。


   C#


using Microsoft.Extensions.DependencyInjection;


   1


This statement allows the app to use dependency injection to register new services.

此语句允许应用程序使用依赖项注入来注册新服务。


7.Insert this segment just above the app.Run(); method:

将这部分插入app.Run();方法的的正上方。


   C#


...

// Initialize the database

var scopeFactory = app.Services.GetRequiredService<IServiceScopeFactory>();

using (var scope = scopeFactory.CreateScope())

{

   var db = scope.ServiceProvider.GetRequiredService<PizzaStoreContext>();

   if (db.Database.EnsureCreated())

   {

       SeedData.Initialize(db);

   }

}


app.Run();


  


This change creates a database scope with the PizzaStoreContext and, if there isn't a database already created, calls the SeedData static class to create one.

此更改使用PizzaStoreContext创建一个数据库作用域,如果尚未创建数据库,则调用SeedData静态类创建一个。


At the moment, the app won't work, as we haven't initialized the PizzaStoreContext. This code should be added to Startup.cs.

目前,该应用程序无法工作,因为我们尚未初始化PizzaStoreContext。此代码应添加到Startup.cs。(在6.0正式版本中,已经没有Startup.cs文件,此代码应添加到Program.cs文件中!)


8.In the Add Services to the container section higher in the Program.cs file, add this code under the current services:

在Program.cs文件中靠上的位置“将服务添加到容器”部分中,将此代码添加到当前服务下:


   C#


builder.services.AddHttpClient();

builder.services.AddDbContext<PizzaStoreContext>(options =>

     options.UseSqlite("Data Source=pizza.db"));


 


This code registers two services. The first AddHttpClient statement will allow the app to access HTTP commands, the app will use an HttpClient to get the JSON for pizza specials. The second registers the new PizzaStoreContext and provides the filename for the SQLite database.

此代码注册两个服务。第一条AddHttpClient语句将允许应用程序访问HTTP命令,应用程序将使用HttpClient获取specials的JSON数据。第二个注册新的PizzaStoreContext,并为SQLite数据库提供文件名。


9.Visual Studio Code will highlight UseSqlite as an error, so you must add a reference to the EntityFrameworkCore package. At the top of the file, under the existing using block add:

如果你不添加EntityFrameworkCore包的引用,Visual Studio Code将高亮显示UseSqlite错误。因此必须在文件顶部的“现有using块”下添加引用:


   C#


using Microsoft.EntityFrameworkCore;


 


Use the database to display pizzas

使用数据库呈现比pizzas


We can now replace the hard-coded pizza in the index.razor page.

我们现在可以替换掉index.razor页面中硬编码的pizza。

1.In the explorer, select Index.razor.

在资源管理器中,选择Index.razor。

2.Replace the existing OnInitialized() method with:

将现有的OnInitialized()方法替换为:


   C#


protected override async Task OnInitializedAsync()

{

   specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>(NavigationManager.BaseUri + "specials");

}


 


备注

This code has replaced OnInitialized() with OnInitializedAsync(). Specials are now going to be returned as JSON from the app asynchronously.

将OnInitialized()代码替换为OnInitializedAsync()。那么Specials现在将作为JSON数据从应用程序异步返回。


3.There are some errors that need fixing. Add these @inject statements under the @page directive.

这里又出现了一些错误需要修复。在 @page 指令下添加这些 @inject 语句。


   razor


@inject HttpClient HttpClient

@inject NavigationManager NavigationManager


 


4.To fix the last error, we need to make the app aware of GetFromJsonAsync.

要修复最后一个错误,我们需要让应用程序知道GetFromJsonAsync。

5.In the explorer, select _Imports.razor.

在资源管理器中,选择 _Imports.razor。

6.Add this new @using statement at the bottom.

把这个新@using语句添到其他@using语句的下面。


   razor


@using System.Net.Http.Json


  


7.Press F5 or select Run and then Start Debugging.There's a runtime error when running the app. The JsonReader raised an exception.

按F5或选择运行,然后开始调试。运行应用程序时出现运行时错误。JsonReader引发了一个异常。

8.Remember that the app should be creating JSON at http://localhost:5000/specials. Navigate to that URL.

请记住,应用程序通过http://localhost:5000/specials的URL返回创建的JSON数据,应导航至此。

9.The app doesn't know how to route this request. You will learn about routing in the module on Blazor routing. Let's fix the error now.

应用程序不知道此请求该如何路由。是时候该了解Blazor路由模块中的路由用法了。让我们现在修复错误。

10.Press Shift + F5, or select Stop Debugging.

按Shift+F5,或选择停止调试。

11.In the explorer, select Program.cs.

在资源管理器中,选择Program.cs。

12.Near the bottom of the file, after the Configure the HTTP request pipeline comment and the app.UseEndpoints block add this endpoint:

在文件底部附近,在配置管道的节点HTTP请求和app.UseEndpoints块之后添加此端点:


   C#


app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");


  


The code should now be:

代码现在应该是这样的:


   C#


...

app.MapRazorPages();

app.MapBlazorHub();

app.MapFallbackToPage("/_Host");

app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

...




13.Press F5 or select Run and then Start Debugging.

按F5或选择运行,然后开始调试。

14.The app should now work, but let's check that the JSON is being created correctly.

应用程序现在应该可以工作了,但是让我们检查一下JSON是否被正确创建。

15.Navigate to http://localhost:5000/specials to see:

导航到http://localhost:5000/specials 会看到下图

5-returned-json

The JSON has the pizzas listed in price descending order as specified in the special pizza controller.

JSON按照Specials控制器中指定的降价顺序列出pizza。

5-more-blazing-pizzas

第六节 Share data in Blazor applications(在Blazor应用程序中共享数据)


Blazor includes several ways to share information between components. You can use component parameters or cascading parameters to send values from a parent component to a child component. The AppState pattern is another approach you can use to store values and access them from any component in the application.

Blazor包括几种在组件之间共享信息的方法。你可以使用组件参数或级联参数将值从父组件发送到子组件。AppState模式是可用于存取值并从应用程序中的任何组件访问这些值的另一种方法。


Suppose you're working on the new pizza delivery website. Multiple pizzas should be displayed on the home page in the same way. You'd like to display the pizzas by rendering a child component for each pizza. Now, you want to pass an ID to that child component that determines the pizza it will display. You also want to store and display a value on multiple components that shows the total number of pizzas you've sold today.

假设你正在开发一个新的pizza外卖网站。多个pizzas应该以相同的方式显示在主页上。你想用子组件来呈现每一个pizza。现在,你希望将一个ID传递给该子组件,该组件决定将要显示那些pizza。你还希望在多个组件上存储和显示一个值,这个值是pizza今天的销售总数。


In this unit, you'll learn three different techniques you can use to share values between two or more Blazor components.

在这个单元中,你将学习三种不同的技术来共享两个或更多Blazor组件之间的值。


Share information with other components by using component parameters

通过使用组件参数与其他组件共享信息


In a Blazor web app, each component renders a portion of HTML. Some components render a complete page but others render smaller fragments of markup, such as a table, a form, or a single control. If your component renders only a section of markup, you must use it as a child component within a parent component. Your child component can also be parent to other, smaller components that render within it. Child components are also known as nested components.

在Blazor web应用程序中,每个组件都呈现HTML的一部分。有些组件呈现一个完整的页面,但另一些组件则呈现较小的标记片段,如表、表单或单个控件。如果组件只呈现一部分标记,则必须将其用作父组件中的子组件。子组件也可以是在其中呈现的其他较小组件的父组件。子组件也被称为嵌套组件。


In this hierarchy of parent and child components, you can share information between them by using component parameters. Define these parameters on child components, then set their values in the parent. For example, if you have a child component that displays pizza photos, you could use a component parameter to pass the pizza ID. The child component looks up the pizza from the ID and obtains pictures and other data. If you want to display many different pizzas, you can use this child component multiple times on the same parent page, passing a different ID to each child.

在父组件和子组件的这个层次结构中,你可以通过使用组件参数在它们之间共享信息。在子组件上定义这些参数,然后在父组件中设置其值。例如,如果你有一个显示pizza照片的子组件,则可以使用组件参数来传递pizza ID。子组件通过ID查找pizza,并获取图片和其他数据。如果你想呈现许多不同的pizza,您可以在同一父页面上多次使用这个子组件,并向每个子组件传递一个不同的ID。


Start by defining the component parameter in the child component. You define it as a C# public property and decorate it with the [Parameter] attribute:

首先,在子组件中定义组件参数。你将其定义为C#公共属性,并使用[Parameter]属性装饰它:


   razor


<h2>New Pizza: @PizzaName</h2>


<p>@PizzaDescription</p>


@code {

[Parameter]

public string PizzaName { get; set; }


[Parameter]

public string PizzaDescription { get; set; } = "The best pizza you've ever tasted."

}


 


Notice that, because the component parameters are members of the child component, you can render them in your HTML by using Blazor's reserved @ symbol, followed by their name. Also, the above code specifies a default value for the PizzaDescription parameter. This value will be rendered if the parent component doesn't pass a value. Otherwise, it will be overridden by the value passed from the parent.

请注意,由于组件参数是子组件的成员,因此你可以使用Blazor的保留符号@,后面跟着它们的名称,在HTML中呈现它们。此外,上面的代码为PizzaDescription参数指定了一个默认值。如果父组件没有传递一个值,则将呈现此值。否则,它将被从父级传递的值所覆盖。


You can also use custom classes in your project as component parameters. Consider this class that describes a topping:

你还可以使用项目中的自定义类作为组件参数。思考一下这个描述配料的类:


   C#


public class PizzaTopping

{

public string Name { get; set; }

public string Ingredients { get; set; }

}




In the parent component, set parameter values by using attributes of the child component's tags. Set simple components directly. With a parameter based on a custom class, use inline C# code to create a new instance of that class and set its values:

在父组件中,通过使用子组件标签的属性来设置参数值。直接设置简单组件。对于基于自定义类的参数,请使用内联C#代码创建该类的新实例,并设置其值:


   razor


@page "/pizzas-toppings"


<h1>Our Latest Pizzas and Topping</h1>


<Pizza PizzaName="Hawaiian" PizzaDescription="The one with pineapple" />


<PizzaTopping Topping="@(new PizzaTopping() { Name = "Chilli Sauce", Ingredients = "Three kinds of chilli." })" />


 


Share information by using cascading parameters

通过使用级联参数来共享信息


Component parameters work well when you want to pass a value to the immediate child of a component. However, things become awkward when you have a deep hierarchy with children of children and so on. Component parameters are not automatically passed to grandchild components from ancestor components or further down the hierarchy. To handle this problem elegantly, Blazor includes cascading parameters. When you set the value of a cascading parameter in a component, its value is automatically available to all descendant components to any depth.

当你要将值从父组件传递给直接子级组件时,组件参数工作良好。然而,当父组件和子组件的子组件有一个更深层次结构时,事情就会变得尴尬了。组件参数不会自动越级向下传递。为了优雅地处理这个问题,Blazor包含了级联参数。在组件中设置级联参数的值时,其值将自动适用于所有后代组件。


In the parent component, use the <CascadingValue> tag to specify the information that will cascade to all descendants. This tag is implemented as a built-in Blazor component. Any component that is rendered within that tag will be able to access the value.

在父组件中,使用<CascadingValue>标签来指定将级联到所有后代的信息。此标签被实现为一个内置的Blazor组件。任何组件都将能够访问在该标签中呈现的值。


   C#


@page "/specialoffers"


<h1>Special Offers</h1>


<CascadingValue Name="DealName" Value="Throwback Thursday">

<!-- Any descendant component rendered here will be able to access the cascading value.

任何后代组件都可以呈现此处的级联值-->

</CascadingValue>


  


In the descendant components, you can access the cascading value by using component members and decorating them with the [CascadingParameter] attribute.

在后代组件中,你可以使用[CascadingParameter] 属性装饰组件成员来访问级联值。


   razor


<h2>Deal: @DealName</h2>


@code {

[CascadingParameter(Name="DealName")]

private string DealName { get; set; }

}


  


So this in example, the <h2> tag will have the content Deal: Throwback Thursday because that cascading value was set by an ancestor component.

所以在这个例子中,<h2>标签将包含内容 Deal: Throwback Thursday ,因为该级联值是由一个父组件设置的。


备注

As for component parameters, you can pass objects as cascading parameters if you have more complex requirements.

对于组件参数,如果你有更复杂的需求,则可以将对象作为级联参数传递。


In the above example, the cascading value is identified by the Name attribute in the parent, matched to the Name value in the [CascadingParameter] attribute. You can optionally omit these names, in which case the attributes will be matched by type. This works well when you only have one parameter of that type. If you want to cascade two different string values, you must use parameter names to avoid any ambiguity.

在上面的示例中,级联值由父属性中的Name属性进行标识,并与[CascadingParameter]属性中的Name值相匹配。你可以选择省略这些名称,在这种情况下,这些属性将按类型进行匹配。当你只有一个该类型的参数时,这工作得很好。如果要级联两个不同的字符串值,则必须使用参数名以避免出现任何歧义。


Share information by using AppState

通过使用“AppState”来共享信息


Another approach to sharing information between different components is to use the AppState pattern. You create a class that defines the properties you want to store and the register it as a scoped service. In any component where you want to set or use the AppState values, you inject the service and then you can access its properties. Unlike component parameters and cascading parameters, values in AppState are available to all components in the application, even components that are not children of the component that stored the value.

在不同组件之间共享信息的另一种方法是使用AppState模式。你可以创建一个类来定义要存储的属性,并将其注册为作用域服务。在要设置或使用AppState值的任何组件中,都可以注入服务,然后就可以访问其属性。与组件参数和级联参数不同,AppState中的值适用于应用程序中的所有组件,甚至不是存储该值组件的子组件。

As an example, let's create a class that stores a value about sales:

例如,让我们创建一个存储有关销售值的类:


   C#


public class PizzaSalesState

{

public int PizzasSoldToday { get; set; }

}


  


Now, add the class as a scoped service, in the Program.cs file:

现在,在Program.cs文件中添加该类的作用域服务:


   C#


...// Add services to the container

builder.services.AddRazorPages();

builder.services.AddServerSideBlazor();

// Add the AppState class

builder.services.AddScoped<PizzaSalesState>();

...




Now, in any component where you want to set or retrieve AppState values, inject the class and then access properties:

现在,任何组件想要设置或检索AppState值,必须先注入类,然后才能访问其属性:


   razor


@page "/"


@inject PizzaSalesState SalesState


<h1>Welcome to Blazing Pizzas</h1>


<p>Today, we've sold this many pizzas: @SalesState.PizzasSoldToday</p>


<button @onclick="IncrementSales">Buy a Pizza</button>


@code {

private void IncrementSales()

{

SalesState.PizzasSoldToday++;

}

}


 


备注

This code implements a counter that increments when the user clicks a button, much like the example in the Blazor Tutorial - Build your first Blazor app. The difference is that in this case, because we've stored the counter's value in an AppState scoped service, the count persists across page loads and can be seen by other users.

这段代码实现了一个计数器,当用户单击按钮时<p>标签呈现的SalesState.PizzasSoldToday值就会增加1,就像Blazor教程中的示例——构建第一个Blazor应用程序。不同之处在于,在这种情况下,因为我们已经将计数器的值存储在AppState作用域服务中,因此计数在整个页面加载中持续存在,并且可以被其他用户看到。


Check your knowledge


1. Blazor components can receive input using two types of Parameters, what are they?


A.Parameter and DescendingParameter


B.Parameter and RequiredParameter


C.Parameter and CascadingParameter


2. AppState can be registered with the service locator using which of these statements?


A.builder.Services.AddAppState()


B.builder.Services.AddServerSideBlazor()


C.builder.Services.AddScoped<PizzaSalesState>()


答案:1( C ) , 2( C )

第七节Exercise - Share data in Blazor applications(练习-在Blazor应用程序中共享数据)


Now that the app is connected to a database it's time to add the ability to order and configure a customer's pizza.

现在该应用程序已经连接到一个数据库,是时候添加配置和订购客户pizza的功能了。


Blazing Pizza would like you to build the ability for customers to change the size of their special pizzas. You need to store the order, and you've chosen to store the application state in a container service.

Blazing Pizza希望你建立可以让顾客改变他们的special pizzas尺寸的功能。你需要存储订单,并且你已选择在容器服务中存储应用程序状态。


In this exercise you'll pass data to a new order configuration component, and see how to store the app's state in an OrderState scoped service.

在本练习中,你将把数据传递给一个新的订单配置组件,并查看如何在OrderState作用域服务中存储该应用程序的状态。


Add a new order configuration dialog

添加一个新的订单配置的对话框


1.In Visual Studio Code, right-click on the Shared folder, and select New File.

在VisualStudio Code中,右键单击Shared文件夹,然后选择“新建文件”。

2.Enter ConfigurePizzaDialog.razor as the filename.

输入ConfigurePizzaDialog.razor作为文件名。

3.Enter this code for the UI of the new ordering component.

为新订购组件的UI输入此代码。


   razor


@inject HttpClient HttpClient

<div class="dialog-container">

   <div class="dialog">

       <div class="dialog-title">

           <h2>@Pizza.Special.Name</h2>

           @Pizza.Special.Description

       </div>

       <form class="dialog-body">

           <div>

               <label>Size:</label>

               <input type="range" min="@Pizza.MinimumSize" max="@Pizza.MaximumSize" step="1" />

               <span class="size-label">

                   @(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice()))

               </span>

           </div>

       </form>


       <div class="dialog-buttons">

           <button class="btn btn-secondary mr-auto" >Cancel</button>

           <span class="mr-center">

               Price: <span class="price">@(Pizza.GetFormattedTotalPrice())</span>

           </span>

           <button class="btn btn-success ml-auto" >Order ></button>

       </div>

   </div>

</div>


 


This component is a dialog that shows the selected special pizza and allows the customer to select the pizza size.

此组件是一个对话框,其中显示所选的special pizza,并允许客户选择pizza的尺寸。


The component needs a special pizza from the index page component to access a pizza's member values.

该组件需要 index页面组件中的一个special pizza才能访问pizza的成员值。


4.Add the Blazor @code block to allow parameters to be passed into the component.

添加Blazor @code,以允许将参数传递到组件中。


   razor


@code {

[Parameter] public Pizza Pizza { get; set; }

}




When a customer selects a pizza the dialog should allow them to change the size of their pizza. Let's enhance the index.razor control to add this interactivity.

当顾客选择一个pizza时,对话框应该允许改变被选中的pizza尺寸。让我们增强index.razor控制来添加这种交互性。


1.In the explorer, expand Pages and then select Index.razor.

在资源管理器中,展开Pages,然后选择Index.razor。

2.Add this code under in the @code block under the List<PizzaSpecial> variable.

在@code块 List<PizzaSpecial>变量的下面添加代码。


   c#


  Pizza configuringPizza;

  bool showingConfigureDialog;


 


3.Add code to create a pizza under the OnInitializedAsync() method.

在OnInitializedAsync()方法下添加代码创建pizza。


   C#


   void ShowConfigurePizzaDialog(PizzaSpecial special)

   {

       configuringPizza = new Pizza()

       {

           Special = special,

           SpecialId = special.Id,

           Size = Pizza.DefaultSize

       };


       showingConfigureDialog = true;

   }


 


4.Allow the webpage to call the server-side ShowConfigurePizzaDialog method by allowing customers to select a pizzas <li> tag. Replace the <li> line with this code:

允许客户通过网页调用服务器端ShowConfigurePizzaDialog方法来选择一个pizza<li>标签。用以下代码替换<li>行:


   razor



<li @onclick="@(() => ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')">


 


When a customer selects a pizza, the server executes the ShowConfigurePizzaDialog method that creates a pizza with the special pizza data and sets the showingConfigureDialog variable to true.

当客户选择pizza时,服务器将执行ShowConfigurePizzaDialog方法,该方法使用special pizza数据创建pizza然后把ShowConfigurePizzaDialog变量设置为true。


5.The page needs a way to display the new ConfigurePizzaDialog component. Add this code just above the @code block:

该页面需要一种手段来显示新的ConfigurePizzaDialog组件。请在@code块的上方添加此代码:


   razor


@if (showingConfigureDialog){

   <ConfigurePizzaDialog Pizza="configuringPizza" />}


 


The whole index.razor file should now look like this:

完整的index.razor文件现在应该是这样的:


   razor


   @page "/"

   @inject HttpClient HttpClient

   @inject NavigationManager NavigationManager


   <div class="main">

     <h1>Blazing Pizzas</h1>

     <ul class="pizza-cards">

       @if (specials != null)

       {

         @foreach (var special in specials)

         {

           <li @onclick="@(() => ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')">

             <div class="pizza-info">

             <span class="title">@special.Name</span>

             @special.Description

             <span class="price">@special.GetFormattedBasePrice()</span>

             </div>

           </li>

         }

       }

     </ul>

   </div>


   @if (showingConfigureDialog)

   {

       <ConfigurePizzaDialog Pizza="configuringPizza" />

   }


   @code {

     List<PizzaSpecial> specials = new();

     Pizza configuringPizza;

     bool showingConfigureDialog;


     protected override async Task OnInitializedAsync()

     {

         specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>(NavigationManager.BaseUri + "specials");

     }


     void ShowConfigurePizzaDialog(PizzaSpecial special)

     {

         configuringPizza = new Pizza()

         {

             Special = special,

             SpecialId = special.Id,

             Size = Pizza.DefaultSize

         };


         showingConfigureDialog = true;

     }

}


  


6.Press F5 or select Run and then Start Debugging.

按F5或选择运行,然后开始调试。

7.Select a pizza and watch the new dialog appear.

选择一个pizza,并观看显示的新对话框。

5-new-pizza-dialog

Handle the state of an order

处理订单的状态


At the moment, the app shows the configuration dialog but doesn't allow you to cancel or move on to ordering the pizza. To manage the state of the order, you'll add a new order state container service.

目前,该应用程序显示了配置对话框,但不允许你取消或继续订购pizza。若要管理订单的状态,你还需要添加一个新的订单状态容器服务。


1.Select the File menu, then New File.

依次选择“文件”菜单,然后选择“新建文件”。

2.For the language, select C#.

请选择c#语言。

3.Enter this code for the class.

为该类输入此代码。


   C#


using System.Collections.Generic;

namespace BlazingPizza

{

   public class OrderState

   {

       public bool ShowingConfigureDialog { get; private set; }

       public Pizza ConfiguringPizza { get; private set; }

       public Order Order { get; private set; } = new Order();


       public void ShowConfigurePizzaDialog(PizzaSpecial special)

       {

           ConfiguringPizza = new Pizza()

           {

               Special = special,

               SpecialId = special.Id,

               Size = Pizza.DefaultSize,

               Toppings = new List<PizzaTopping>(),

           };


           ShowingConfigureDialog = true;

       }


       public void CancelConfigurePizzaDialog()

       {

           ConfiguringPizza = null;


           ShowingConfigureDialog = false;

       }


       public void ConfirmConfigurePizzaDialog()

       {

           Order.Pizzas.Add(ConfiguringPizza);

           ConfiguringPizza = null;


           ShowingConfigureDialog = false;

       }

   }

}


 


You'll see that there's a lot of code currently in the index.razor component that we can move into the new class. The next step is to make this service available in the app.

你将看到,当前在index.razor组件中有很多代码,我们可以移动到新的类中。下一步是在应用程序中提供这项服务。


4.Press CTRL+S. In the Save As dialog, for File name enter OrderState.cs, then select Save.

按CTRL+S键。在“另存为”对话框中,输入“文件名”OrderState.cs,然后选择“保存”。

5.In the explorer, select Program.cs

在资源管理器中,选择 Program.cs。

6.In the Add services to the container segment, add this line at the bottom.

在添加服务到容器的段落底部添加这一行。


   C#



builder.services.AddScoped<OrderState>();


 


From the previous exercise, we added our database context here. This code adds the new OrderState service. With this in place, we can now use it in the index.razor component.

在前面的练习中,我们在这里添加了数据库上下文。此代码添加了新的OrderState服务。有了这个服务,现在我们可以在index.razor组件中使用它了。

7.In the explorer, expand Pages and then select Index.razor.

在资源管理器中,展开页面,然后选择Index.razor。

8.At the top of the file, under the NavigationManager, add this code:

在文件顶部的NavigationManager下,添加以下代码:


   razor


@inject OrderState OrderState


   1


Now you can delete all the code that is in the order state. The @code block should look like this.

现在您可以删除订单状态里的所有代码。@code块应该是这样的。


   C#


@code {

 List<PizzaSpecial> specials = new List<PizzaSpecial>();


 protected override async Task OnInitializedAsync()

 {

     specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>(NavigationManager.BaseUri + "specials");

 }

}


 


There are now errors where all the code references what's been deleted.

现在有一些 now errors,因为所有代码都引用了已删除的内容。

9.Change the call to ShowConfigurePizzaDialog(special)) to use the OrderState version:

更改对ShowConfigurePizzaDialog(special))的调用,现在使用OrderState版本:


   razor


<li @onclick="@(() => OrderState.ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')">


   1


10.Change the reference to the boolean showingConfigureDialog:

更改对boolean showingConfigureDialog的引用:


   razor


<ConfigurePizzaDialog Pizza="OrderState.ConfiguringPizza" />


   1


12.Press F5 or select Run and then Start Debugging.

按F5或选择运行,然后开始调试。


Cancel and make pizza orders

取消和订购pizza


You may have noticed in the OrderState class two methods that we haven't used yet. CancelConfigurePizzaDialog and ConfirmConfigurePizzaDialog will close the dialog and add the pizza to an Order object if the customer has confirmed the order. Let's connect these methods to the configuration dialog buttons.

你可能已经注意到OrderState类中有两个我们尚未使用的方法。CancelConfigurePizzaDialog和ConfirmConfigurePizzaDialog将关闭对话框,并且客户确认订单后,pizza将被添加到订单对象中。现在我们将这些方法连接到配置对话框按钮。


1.In the explorer, expand Shared and then select ConfigurePizzaDialog.razor.

在资源管理器中,展开 Shared文件夹,然后选择ConfigurePizzaDialog.razor。

2.In the @code block add two new parameters:

在@code块中添加两个新参数:


   razor


 @code {

   [Parameter] public Pizza Pizza { get; set; }

   [Parameter] public EventCallback OnCancel { get; set; }

   [Parameter] public EventCallback OnConfirm { get; set; }

 }


  


3.The buttons can now have @onclick directives added. Change the current code for the dialog buttons to this markup:

这些按钮现在可以添加@onclick指令。将对话框按钮的代码更改为如下标记:


   razor


 <div class="dialog-buttons">

     <button class="btn btn-secondary mr-auto" @onclick="OnCancel">Cancel</button>

     <span class="mr-center">

         Price: <span class="price">@(Pizza.GetFormattedTotalPrice())</span>

     </span>

     <button class="btn btn-success ml-auto" @onclick="OnConfirm">Order ></button>

 </div>


 


4.The last step is to pass our OrderState methods for canceling and confirming orders. In the explorer, expand Pages and then select Index.razor.

最后一步是传递OrderState方法的取消和确认订单命令。在资源管理器中,展开页面,然后选择Index.razor。


   razor


<ConfigurePizzaDialog

     Pizza="OrderState.ConfiguringPizza"

     OnCancel="OrderState.CancelConfigurePizzaDialog"

     OnConfirm="OrderState.ConfirmConfigurePizzaDialog" />


  


6.Press F5 or select Run and then Start Debugging.

按F5或选择运行,然后开始调试。

The app should now let customers cancel or add a configured pizza to an order. We have no way to show the current order or update the prices when the pizza size is changed. We'll add these features in the next exercise.

应用程序现在允许客户取消订单或向订单中添加已配置的pizza。但是当pizza尺寸改变时,我们还是无法显示当前订单和对话框中更新的价格。这些功能特性还是留给下一节的练习来实现吧。