练习 - 在 Blazor 应用程序中共享数据

已完成

现在,你的应用已连接到数据库,现在可以添加客户配置和订购披萨的功能。

Blazing Pizza 希望你能够为客户构建改变其特殊比萨饼的大小的能力。 需要存储订单,并且想要将应用程序状态存储在容器服务中。

在本练习中,你将数据传递到新的订单配置组件,并了解如何将应用的状态存储在 OrderState 范围内的服务中。

添加新订单配置对话框

  1. 如果应用仍在运行,请停止该应用。

  2. 在 Visual Studio Code 中,右键单击 共享文件夹 并选择“ 新建文件”。

  3. 输入 ConfigurePizzaDialog.razor 作为文件名。

  4. 输入新排序组件的 UI 的以下代码:

    @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>
    

    此组件是一个对话框,显示所选的特殊披萨,并允许客户选择披萨大小。

    该组件需要索引页组件中的特价披萨才能访问披萨的成员值。

  5. 添加 Blazor @code 块以允许将参数传递到组件:

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

订购披萨

当客户选择披萨时,对话框应允许他们更改披萨的大小。 让我们增强 index.razor 控件以添加此交互性。

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

  2. 将这段代码添加到@code变量下的List<PizzaSpecial>块中:

    Pizza configuringPizza;
    bool showingConfigureDialog;
    
  3. 请将此代码添加到 OnInitializedAsync() 方法中以创建披萨:

    void ShowConfigurePizzaDialog(PizzaSpecial special)
    {
        configuringPizza = new Pizza()
        {
            Special = special,
            SpecialId = special.Id,
            Size = Pizza.DefaultSize
        };
    
        showingConfigureDialog = true;
    }
    
  4. 通过允许客户选择披萨的 ShowConfigurePizzaDialog 标记,允许网页调用服务器端 <li> 方法。 将 <li> 行替换为此代码:

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

    当客户选择披萨时,服务器将执行ShowConfigurePizzaDialog使用特殊披萨数据创建披萨的方法,并将变量showingConfigureDialog设置为 true

  5. 页面需要一种方法来显示新 ConfigurePizzaDialog 组件。 将此代码添加到@code块之上:

    @if (showingConfigureDialog)
    {
        <ConfigurePizzaDialog Pizza="configuringPizza" />
    }
    

    整个 index.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. 选择 F5 或选择 “运行”。 然后选择“ 开始调试”。

  7. 选择披萨并观看新对话框出现。

    显示披萨订购对话框的屏幕截图。

处理订单的状态

目前,应用会显示配置对话框,但不允许取消或继续订购披萨。 若要管理订单的状态,请添加新订单状态容器服务。

  1. 如果应用仍在运行,请停止该应用。

  2. BlazingPizza 文件夹中创建新文件夹。 将其命名为服务

  3. Services 文件夹中创建新文件。 将其命名为OrderState.cs

  4. 输入类的以下代码:

    namespace BlazingPizza.Services;
    
    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;
        }
    }
    

    你注意到 index.razor 组件中现有代码,我们可以将其移入新类中。 下一步是使此服务在应用中可用。

  5. 在文件资源管理器中,选择 Program.cs

  6. 在文件中以builder.Services.开头的行所在的部分,添加这一行:

    builder.Services.AddScoped<OrderState>();
    

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

  7. 将以下 using 指令添加到文件顶部。 此指令解析对 OrderState 类的任何引用:

    using BlazingPizza.Services;
    
  8. 在文件资源管理器中,展开 Pages,然后选择 Index.razor

  9. 在文件顶部的下面 @inject NavigationManager NavigationManager,添加以下代码:

    @using BlazingPizza.Services
    @inject OrderState OrderState
    
  10. configuringPizza块中删除showingConfigureDialogShowConfigurePizzaDialog()@code。 该类现在应如下所示:

    @code {
        List<PizzaSpecial> specials = new List<PizzaSpecial>();
    
        protected override async Task OnInitializedAsync()
        {
            specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>(NavigationManager.BaseUri + "specials");
        }
    }
    

    现在,无论代码引用已删除的元素,都会出现错误。

  11. 将对 ShowConfigurePizzaDialog(special)) 的调用更改为使用 OrderState 版本:

    <li @onclick="@(() => OrderState.ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')">
    
  12. 更改对布尔值的 showingConfigureDialog引用:

    @if (OrderState.ShowingConfigureDialog)
    
  13. 通过使用 configuringPizza 更改参数。

    <ConfigurePizzaDialog Pizza="OrderState.ConfiguringPizza" />
    
  14. 选择 F5 或选择 “运行”。 然后选择“ 开始调试”。

    如果一切正确,则不应看到任何差异。 对话框像以前一样显示。

取消和进行披萨订购

你可能会注意到 OrderState 类中有两种方法尚未使用。 CancelConfigurePizzaDialogConfirmConfigurePizzaDialog方法关闭对话框,并在客户确认订单后将披萨添加到Order对象中。 让我们将这些方法连接到配置对话框按钮。

  1. 如果应用仍在运行,请停止该应用。

  2. 在文件资源管理器中,展开“共享”。 然后选择 ConfigurePizzaDialog.razor

  3. @code 块中,添加两个新参数:

    @code {
       [Parameter] public Pizza Pizza { get; set; }
       [Parameter] public EventCallback OnCancel { get; set; }
       [Parameter] public EventCallback OnConfirm { get; set; }
    }
    `` `
    
    
  4. 现在可以在按钮上添加@onclick指令。 将对话框按钮的当前代码更改为此标记:

    <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>
    
  5. 最后一步是传递用于取消和确认订单的 OrderState 方法。 在文件资源管理器中,展开“页面”。 然后选择“Index.razor”

  6. 更改对 ConfigurePizzaDialog 组件的调用的代码:

    <ConfigurePizzaDialog
        Pizza="OrderState.ConfiguringPizza"
        OnCancel="OrderState.CancelConfigurePizzaDialog"
        OnConfirm="OrderState.ConfirmConfigurePizzaDialog" />
    
  7. 选择 F5 或选择 “运行”。 然后选择“ 开始调试”。

应用现在应允许客户取消或向订单添加配置的披萨。 在披萨大小更改时,我们无法显示当前订单或更新价格。 我们将在下一个练习中添加这些功能。