Exercice - Changer la navigation dans votre application Blazor en utilisant la directive @page

Effectué

L’aide de l’état de navigation de l’application Blazor aide le code C# à gérer les URI d’une application. Il y a également un composant NavLink qui est un remplacement de l'élément <a>. L’une des fonctionnalités d’NavLink consiste à ajouter une classe active aux liens HTML pour les menus d’une application.

Votre équipe a commencé à utiliser l’application de pizza Blazor et a créé des composants Blazor pour représenter des pizzas et des commandes. L’application doit maintenant avoir une page de validation et d’autres pages liées aux commandes.

Dans cet exercice, vous allez ajouter une nouvelle page de validation, ajouter une navigation en haut de l’application, puis utiliser un composant Blazor NavLink pour améliorer votre code.

Cloner l’application existante de votre équipe

Notes

Ce module utilise l’interface CLI .NET et Visual Studio Code pour le développement local. Une fois ce module terminé, vous pouvez appliquer les concepts en utilisant Visual Studio (Windows) ou Visual Studio pour Mac (macOS). Pour poursuivre le développement, utilisez Visual Studio Code pour Windows, Linux et macOS.

Ce module utilise le SDK .NET 6.0. Assurez-vous que vous avez installé .NET 6.0 en exécutant la commande suivante dans votre terminal préféré :

dotnet --list-sdks

Une sortie similaire à ce qui suit s’affiche :

3.1.100 [C:\program files\dotnet\sdk]
5.0.100 [C:\program files\dotnet\sdk]
6.0.100 [C:\program files\dotnet\sdk]

Vérifiez que la liste comporte une version commençant par 6. S’il n’y en a pas ou que la commande est introuvable, installez la dernière version du kit SDK .NET 6.0.

Si vous n’avez jamais créé d’application Blazor auparavant, suivez les instructions d’installation de Blazor pour installer la bonne version de .NET et vérifier que votre machine est correctement configurée. Arrêtez à l’étape Créer votre application.

  1. Ouvrez Visual Studio Code.

  2. Ouvrez le terminal intégré à partir de Visual Studio Code en sélectionnant Affichage. Ensuite, dans le menu principal, sélectionnez Terminal.

  3. Dans le terminal, accédez à l’emplacement où vous voulez créer le projet.

  4. Clonez l’application à partir de GitHub.

    git clone https://github.com/MicrosoftDocs/mslearn-blazor-navigation.git BlazingPizza
    
  5. Sélectionnez Fichier, puis Ouvrir un dossier.

  6. Dans la boîte de dialogue Ouvrir, accédez au dossier BlazingPizza, puis sélectionnez Sélectionner un dossier.

    Visual Studio Code est susceptible d’afficher une invite relative aux dépendances non résolues. Sélectionnez Restaurer.

  7. Exécutez l’application pour vérifier son bon fonctionnement.

  8. Dans Visual Studio Code, sélectionnez F5. Vous pouvez aussi sélectionner Démarrer le débogage dans le menu Exécuter.

    Capture d’écran montrant la version clonée de l’application Blazing Pizza.

    Configurez quelques pizzas et ajoutez-les à votre commande. Sélectionnez Commande > au bas de la page. Vous allez voir le message par défaut « 404 - Introuvable », car l’équipe n’a pas encore créé une page de validation.

  9. Sélectionnez Maj + F5 pour arrêter l’application.

Ajouter une page de validation

  1. Dans Visual Studio Code, dans la fenêtre de l’explorateur de fichiers, sélectionnez App.razor.

    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        <Found Context="routeData">
            <RouteView RouteData="@routeData" />
        </Found>
        <NotFound>
            <LayoutView>
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
    

    Le bloc de code <NotFound> est ce que les clients voient s’ils essaient d’accéder à une page qui n’existe pas.

  2. Dans l’Explorateur de fichiers, développez Pages, cliquez avec le bouton droit sur le dossier, puis sélectionnez Nouveau fichier.

  3. Nommez le nouveau fichier Checkout.razor. Dans ce fichier, écrivez le code suivant :

    @page "/checkout"
    @inject OrderState OrderState
    @inject HttpClient HttpClient
    @inject NavigationManager NavigationManager
    
    <div class="top-bar">
        <a class="logo" href="">
            <img src="img/logo.svg" />
        </a>
    
        <a href="" class="nav-tab">
            <img src="img/pizza-slice.svg" />
            <div>Get Pizza</div>
        </a>
    
    </div>
    
    <div class="main">
        <div class="checkout-cols">
            <div class="checkout-order-details">
                <h4>Review order</h4>
                @foreach (var pizza in Order.Pizzas)
                {
                    <p>
                        <strong>
                            @(pizza.Size)"
                            @pizza.Special.Name
                            (£@pizza.GetFormattedTotalPrice())
                        </strong>
                    </p>
                }
    
                <p>
                    <strong>
                        Total price:
                        £@Order.GetFormattedTotalPrice()
                    </strong>
                </p>
            </div>
        </div>
    
        <button class="checkout-button btn btn-warning">
            Place order
        </button>
    </div>
    
    @code {
        Order Order => OrderState.Order;
    }
    

    Cette page s’appuie sur l’application actuelle et utilise l’état de l’application enregistré dans OrderState. La première div est la navigation dans l’en-tête de l’application. Nous allons l’ajouter à la page d’index.

  4. Dans l’Explorateur de fichiers, développez Pages, puis sélectionnez index.razor.

  5. Au-dessus de la classe <div class="main">, ajoutez le code html top-bar.

    <div class="top-bar">
        <a class="logo" href="">
            <img src="img/logo.svg" />
        </a>
    
        <a href="" class="nav-tab" >
            <img src="img/pizza-slice.svg" />
            <div>Get Pizza</div>
        </a>
    
    </div>
    

    Quand nous sommes sur cette page, ce serait bien de montrer les clients en mettant le lien en surbrillance. L’équipe ayant déjà créé une classe CSS active, ajoutez active à l’attribut class qui contient déjà le style nav-tab.

    <div class="top-bar">
        <a class="logo" href="">
            <img src="img/logo.svg" />
        </a>
    
        <a href="" class="nav-tab active" >
            <img src="img/pizza-slice.svg" />
            <div>Get Pizza</div>
        </a>
    
    </div>
    
  6. Dans Visual Studio Code, sélectionnez F5. Vous pouvez aussi sélectionner Démarrer le débogage dans le menu Exécuter.

    L’application a maintenant une belle barre de menus en haut, incluant le logo de l’entreprise. Ajoutez quelques pizzas et faites progresser la commande jusqu’à la page de validation. Vous allez voir que les pizzas sont listées et que l’indicateur actif est manquant dans le menu.

    Capture d’écran montrant la page de validation avec des pizzas.

  7. Sélectionnez Maj + F5 pour arrêter l’application.

Autoriser les clients à passer une commande

Pour le moment, la page de validation ne permet pas aux clients de passer leur commande. La logique de l’application doit stocker la commande à envoyer à la cuisine. Une fois la commande envoyée, redirigeons les clients vers la page d’accueil.

  1. Dans l’Explorateur de fichiers, développez Pages, puis sélectionnez Checkout.razor.

  2. Modifiez l’élément de bouton pour appeler une méthode PlaceOrder. Ajoutez les attributs @onclick et disabled comme indiqué :

    <button class="checkout-button btn btn-warning" @onclick="PlaceOrder" disabled=@isSubmitting>
      Place order
    </button>
    

    Comme nous ne voulons pas que les clients passent des commandes en doublon, désactivons le bouton Place order (Passer la commande) jusqu’à ce que la commande soit traitée.

  3. Dans le bloc @code, ajoutez ce code sous le code Order Order => OrderState.Order;.

    bool isSubmitting;
    
    async Task PlaceOrder()
    {
        isSubmitting = true;
        var response = await HttpClient.PostAsJsonAsync(NavigationManager.BaseUri + "orders", OrderState.Order);
        var newOrderId= await response.Content.ReadFromJsonAsync<int>();
        OrderState.ResetOrder();
        NavigationManager.NavigateTo("/");
    }
    

    Le code précédent va désactiver le bouton Place order, envoyer le JSON qui va être ajouté à pizza.db, effacer la commande et utiliser NavigationManager pour rediriger le client vers la page d’accueil.

    Vous devez ajouter du code pour gérer la commande. Vous allez ajouter une classe OrderController pour cette tâche. Si vous examinez PizzaStoreContext.cs, vous verrez qu’il n’y a qu’une prise en charge de base de données Entity Framework pour PizzaSpecials. Nous allons tout d’abord le résoudre.

Ajouter la prise en charge d’Entity Framework pour les commandes et les pizzas

  1. Dans l’Explorateur de fichiers, sélectionnez PizzaStoreContext.cs.

  2. Remplacez la classe PizzaStoreContext par ce code :

      public class PizzaStoreContext : DbContext
      {
            public PizzaStoreContext(
                DbContextOptions options) : base(options)
            {
            }
    
            public DbSet<Order> Orders { get; set; }
    
            public DbSet<Pizza> Pizzas { get; set; }
    
            public DbSet<PizzaSpecial> Specials { get; set; }
    
            public DbSet<Topping> Toppings { get; set; }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
    
                // Configuring a many-to-many special -> topping relationship that is friendly for serialization
                modelBuilder.Entity<PizzaTopping>().HasKey(pst => new { pst.PizzaId, pst.ToppingId });
                modelBuilder.Entity<PizzaTopping>().HasOne<Pizza>().WithMany(ps => ps.Toppings);
                modelBuilder.Entity<PizzaTopping>().HasOne(pst => pst.Topping).WithMany();
            }
    
      }
    

    Ce code ajoute la prise en charge d’Entity Framework pour les classes Order et Pizza de l’application.

  3. Dans Visual Studio Code, dans le menu, sélectionnez Fichier>Nouveau fichier texte.

  4. Sélectionnez le langage C# et entrez ce code :

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.EntityFrameworkCore;
    
    namespace BlazingPizza;
    
    [Route("orders")]
    [ApiController]
    public class OrdersController : Controller
    {
        private readonly PizzaStoreContext _db;
    
        public OrdersController(PizzaStoreContext db)
        {
            _db = db;
        }
    
        [HttpGet]
        public async Task<ActionResult<List<OrderWithStatus>>> GetOrders()
        {
            var orders = await _db.Orders
     	    .Include(o => o.Pizzas).ThenInclude(p => p.Special)
     	    .Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
     	    .OrderByDescending(o => o.CreatedTime)
     	    .ToListAsync();
    
            return orders.Select(o => OrderWithStatus.FromOrder(o)).ToList();
        }
    
        [HttpPost]
        public async Task<ActionResult<int>> PlaceOrder(Order order)
        {
            order.CreatedTime = DateTime.Now;
    
            // Enforce existence of Pizza.SpecialId and Topping.ToppingId
            // in the database - prevent the submitter from making up
            // new specials and toppings
            foreach (var pizza in order.Pizzas)
            {
                pizza.SpecialId = pizza.Special.Id;
                pizza.Special = null;
            }
    
            _db.Orders.Attach(order);
            await _db.SaveChangesAsync();
    
            return order.OrderId;
        }
    }
    

    Le code précédent permet à notre application d’obtenir toutes les commandes en cours et de passer une commande. L'attribut Blazor [Route("orders")] permet à cette classe de gérer les requêtes HTTP entrantes pour /orders et /orders/{OrderID}.

  5. Enregistrez vos modifications avec Ctrl+S.

  6. Pour le nom de fichier, utilisez OrderController.cs. Veillez à enregistrer le fichier dans le même répertoire queOrderState.cs.

  7. Dans l’Explorateur de fichiers, sélectionnez OrderState.cs.

  8. En bas de la classe, sous la méthode RemoveConfiguredPizza, modifiez ResetOrder() pour réinitialiser l’ordre :

    public void ResetOrder()
    {
        Order = new Order();
    }
    

Tester la fonctionnalité de validation

  1. Dans Visual Studio Code, sélectionnez F5. Vous pouvez aussi sélectionner Démarrer le débogage dans le menu Exécuter.

    L’application doit compiler, mais si vous créez une commande et que vous essayez de valider l’achat, vous verrez une erreur d’exécution. L’erreur se produit en raison du fait que notre base de données SQLite pizza.db a été créée avant la prise en charge des commandes et des pizzas. Nous devons supprimer le fichier pour qu’une nouvelle base de données puisse être créée correctement.

  2. Sélectionnez Maj + F5 pour arrêter l’application.

  3. Dans l’Explorateur de fichiers, supprimez le fichier pizza.db.

  4. Sélectionnez F5. Vous pouvez aussi sélectionner Démarrer le débogage dans le menu Exécuter.

    Faites un test en ajoutant des pizzas, en accédant à la validation et en passant une commande. Vous êtes redirigé vers la page d’accueil et vous voyez que la commande est maintenant vide.

  5. Sélectionnez Maj + F5 pour arrêter l’application.

L’application s’améliore. Nous avons une configuration de pizza et une validation. Nous voulons permettre aux clients de voir l’état de leur commande de pizzas après avoir passé la commande.

Ajouter une page de commande

  1. Dans l’Explorateur de fichiers, développez Pages, cliquez avec le bouton droit sur le dossier, puis sélectionnez Nouveau fichier.

  2. Nommez le nouveau fichier MyOrders.razor. Dans ce fichier, écrivez le code suivant :

    @page "/myorders"
    @inject HttpClient HttpClient
    @inject NavigationManager NavigationManager
    
    <div class="top-bar">
        <a class="logo" href="">
            <img src="img/logo.svg" />
        </a>
    
        <a href="" class="nav-tab">
            <img src="img/pizza-slice.svg" />
            <div>Get Pizza</div>
        </a>
    
        <a href="myorders" class="nav-tab active">
            <img src="img/bike.svg" />
            <div>My Orders</div>
        </a>
    </div>
    
    <div class="main">
        @if (ordersWithStatus == null)
        {
            <text>Loading...</text>
        }
        else if (!ordersWithStatus.Any())
        {
            <h2>No orders placed</h2>
            <a class="btn btn-success" href="">Order some pizza</a>
        }
        else
        {
            <div class="list-group orders-list">
                @foreach (var item in ordersWithStatus)
                {
                    <div class="list-group-item">
                        <div class="col">
                            <h5>@item.Order.CreatedTime.ToLongDateString()</h5>
                            Items:
                            <strong>@item.Order.Pizzas.Count()</strong>;
                            Total price:
                            <strong>£@item.Order.GetFormattedTotalPrice()</strong>
                        </div>
                        <div class="col">
                            Status: <strong>@item.StatusText</strong>
                        </div>
                        @if (@item.StatusText != "Delivered")
                        {
                            <div class="col flex-grow-0">
                                <a href="myorders/" class="btn btn-success">
                                    Track &gt;
                                </a>
                            </div>
                        }
                    </div>
                }
            </div>
        }
    </div>
    
    @code {
        List<OrderWithStatus> ordersWithStatus = new List<OrderWithStatus>();
    
        protected override async Task OnParametersSetAsync()
        {
          ordersWithStatus = await HttpClient.GetFromJsonAsync<List<OrderWithStatus>>(
              $"{NavigationManager.BaseUri}orders");
        }
    }
    

    La navigation doit être modifiée sur toutes les pages dont nous disposons maintenant pour inclure un lien vers la nouvelle page My orders (Mes commandes). Ouvrez Checkout.razor et Index.razor, puis remplacez la navigation par ce code :

    <div class="top-bar">
        <a class="logo" href="">
            <img src="img/logo.svg" />
        </a>
    
        <a href="" class="nav-tab active" >
            <img src="img/pizza-slice.svg" />
            <div>Get Pizza</div>
        </a>
    
        <a href="myorders" class="nav-tab" >
            <img src="img/bike.svg" />
            <div>My orders</div>
        </a>
    
    </div>
    

    En utilisant des éléments <a>, nous pouvons gérer manuellement la page qui est active en ajoutant la classe CSS active. Nous allons mettre à jour l’ensemble de la navigation pour utiliser un composant NavLink à la place.

  3. Dans les trois pages avec navigation (Index.razor, Checkout.razor et MyOrders.razor), utilisez le même code Blazor pour la navigation :

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

    La classe CSS active est maintenant automatiquement ajoutée aux pages par le composant NavLink. Vous n’avez pas à vous souvenir de la faire sur chaque page sur laquelle la navigation est activée.

  4. La dernière étape est de modifier NavigationManager pour rediriger vers la page myorders une fois qu’une commande est passée. Dans l’Explorateur de fichiers, développez Pages, puis sélectionnez Checkout.razor.

  5. Changez la méthode PlaceOrder pour rediriger vers la page appropriée en passant /myorders à NavigationManager.NavigateTo() :

    async Task PlaceOrder()
    {
        isSubmitting = true;
        var response = await HttpClient.PostAsJsonAsync($"{NavigationManager.BaseUri}orders", OrderState.Order);
        var newOrderId = await response.Content.ReadFromJsonAsync<int>();
        OrderState.ResetOrder();
        NavigationManager.NavigateTo("/myorders");
    } 
    
  6. Dans Visual Studio Code, sélectionnez F5. Vous pouvez aussi sélectionner Démarrer le débogage dans le menu Exécuter.

    Capture d’écran montrant la page de commande.

    Vous devriez être en mesure de commander des pizzas, puis de voir les commandes qui se trouvent actuellement dans la base de données.

  7. Sélectionnez Maj + F5 pour arrêter l’application.