演習 - ルート パラメーターを使用してアプリのナビゲーションを改善する

完了

Blazor のルート パラメーターによって、URL で渡されたデータにコンポーネントからアクセスできるようになります。 ルート パラメーターを使用すると、注文の OrderId によって、アプリから特定の注文にアクセスできるようになります。

顧客は、特定の注文に関する詳細情報を表示できることを望んでいます。 あなたは、顧客が自分の注文に直接移動されるように、精算ページを更新してゆきます。 その後、現在まだ受け取っていない注文があれば顧客が追跡できるように、注文ページを更新してゆきます。

この演習では、ルート パラメーターを使用する新しい注文詳細ページを追加します。 パラメーターに制約を追加して、正しいデータ型を確認する方法を確認します。

注文詳細ページを作成する

  1. Visual Studio Code のメニューで、[ファイル]>[新しいテキスト ファイル] を選択します。

  2. 言語として ASP.NET Razor を選択します。

  3. このコードを使用して注文詳細ページのコンポーネントを作成します。

    @page "/myorders/{orderId}"
    @inject NavigationManager NavigationManager
    @inject HttpClient HttpClient
    
    <div class="top-bar">
        <a class="logo" href="">
            <img src="img/logo.svg" />
        </a>
    
        <NavLink href="" class="nav-tab" Match="NavLinkMatch.All">
            <img src="img/pizza-slice.svg" />
            <div>Get Pizza</div>
        </NavLink>
    
        <NavLink href="myorders" class="nav-tab">
            <img src="img/bike.svg" />
            <div>My Orders</div>
        </NavLink>
    
    </div>
    
    <div class="main">
        @if (invalidOrder)
        {
            <h2>Order not found</h2>
            <p>We're sorry but this order no longer exists.</p>
        }
        else if (orderWithStatus == null)
        {
            <div class="track-order">
                <div class="track-order-title">
                    <h2>
                      <text>Loading...</text>
                    </h2>
                    <p class="ml-auto mb-0">
                        ...
                    </p>
                </div>
            </div>
        }
        else
        {
            <div class="track-order">
                <div class="track-order-title">
                    <h2>
                        Order placed @orderWithStatus.Order.CreatedTime.ToLongDateString()
                    </h2>
                    <p class="ml-auto mb-0">
                        Status: <strong>@orderWithStatus.StatusText</strong>
                    </p>
                </div>
                <div class="track-order-body">
                    <div class="track-order-details">
                      @foreach (var pizza in orderWithStatus.Order.Pizzas)
                      {
                          <p>
                              <strong>
                                  @(pizza.Size)"
                                  @pizza.Special.Name
                                  (£@pizza.GetFormattedTotalPrice())
                              </strong>
                          </p>
                      }
                    </div>
                </div>
            </div>
        }
    </div>
    
    @code {
        [Parameter] public int OrderId { get; set; }
    
        OrderWithStatus orderWithStatus;
        bool invalidOrder = false;
    
        protected override async Task OnParametersSetAsync()
        {
          try
          {
              orderWithStatus = await HttpClient.GetFromJsonAsync<OrderWithStatus>(
                  $"{NavigationManager.BaseUri}orders/{OrderId}");
          }
          catch (Exception ex)
          {
              invalidOrder = true;
              Console.Error.WriteLine(ex);
          }
        }
    }
    
    

    このページは、MyOrders コンポーネントに似ているように見えます。 OrderController を呼び出していますが、今回は特定の注文を要求しています。 OrderId が一致するものです。 この要求を処理するコードを追加しましょう。

  4. Ctrl+S キーを押して、変更内容を保存します。

  5. ファイル名には OrderDetail.razor を使用します。 このファイルは、Pages ディレクトリに保存するようにしてください。

  6. エクスプローラーで、OrderController.cs を選択します。

  7. PlaceOrder メソッドの下に、注文を状態と一緒に返す新しいメソッドを追加します。

    [HttpGet("{orderId}")]
    public async Task<ActionResult<OrderWithStatus>> GetOrderWithStatus(int orderId)
    {
        var order = await _db.Orders
            .Where(o => o.OrderId == orderId)
            .Include(o => o.Pizzas).ThenInclude(p => p.Special)
            .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
            .SingleOrDefaultAsync();
    
        if (order == null)
        {
            return NotFound();
        }
    
        return OrderWithStatus.FromOrder(order);
    }
    

    このコードによって、Order コントローラーが URL に orderId を格納して HTTP 要求に応答できるようになりました。 このメソッドは次に、この ID を使用してデータベースのクエリを実行し、注文が見つかった場合は OrderWithStatus オブジェクトを返します。

    この新しいページを、顧客が精算を行うときに使用しましょう。Checkout.razor コンポーネントを更新する必要があります。

  8. エクスプローラーで、[ページ] を展開します。 次に、Checkout.razor を選択します。

  9. 行われた注文の注文 ID を使用するように、NavigationManager.NavigateTo("/myorders") の呼び出しを変更します。

    NavigationManager.NavigateTo($"myorders/{newOrderId}");
    

    既存のコードでは既に、注文の実行からの応答として newOrderId をキャプチャしていました。 ここでそれを利用すれば、その注文に直接移動できます。

ルート パラメーターを正しいデータ型に制限する

アプリは、(http://localhost:5000/myorders/6) のような数値の注文 ID を持つ要求にのみ応答する必要があります。 数値以外の注文を使おうとするユーザーがいても止められません。 これを変更してみましょう。

  1. エクスプローラーで、[ページ] を展開します。 次に、OrderDetail.razor を選択します。

  2. コンポーネントで整数のみが受け入れられるように、ルート パラメーターを変更します。

    @page "/myorders/{orderId:int}"
    
  3. これで、(http://localhost:5000/myorders/non-number) に移動しようとした場合、Blazor のルーティングでは URL の一致が見つからず、"ページが見つからない" が返されます。

    ページが見つからないという画面のスクリーンショット。

  4. Visual Studio Code で F5 を選択します。 または [実行] メニューから [デバッグの開始] を選択します。

    1 つの注文の詳細注文ページが表示されているスクリーンショット。

    アプリ、注文、精算の全体を移動します。 詳細注文画面に移動されるので、注文の状態を確認します。

  5. 異なる注文 ID を試します。 有効な注文ではない整数を使用すると、"注文が見つかりません" というメッセージが表示されます。

    整数以外の注文 ID を使用すると、ページが見つかりません。 さらに重要なのは、アプリにハンドルされない例外が発生しない点です。

  6. Shift + F5 キーを選択して、アプリを停止します。

注文ページを更新する

この時点で、My Orders ページには詳細を表示するためのリンクがありますが、URL が間違っています。

  1. エクスプローラーで、[ページ] を展開します。 次に、MyOrders.razor を選択します。

  2. <a href="myorders/" class="btn btn-success"> 要素を次のコードに置き換えます。

    <a href="myorders/@item.Order.OrderId" class="btn btn-success">
    

この演習のために最後のピザの注文を作成することで、これがどのように機能するかをテストできます。 次に [My Orders] を選択し、[Track >] リンクをたどります。