Esercizio: Modificare lo spostamento nell'app Blazor usando la direttiva @page
Blazor include un helper dello stato di navigazione che consente al codice C# di gestire un URI nell'app. È disponibile anche un componente NavLink che rappresenta una soluzione sostitutiva per l'elemento <a>. Una delle funzionalità di NavLink è costituita dall'aggiunta di una classe attiva ai collegamenti HTML per i menu dell'app.
Il tuo team ha avviato l'applicazione Blazing Pizza e ha creato componenti Blazor per rappresentare le pizze e gli ordini. Ora è necessario aggiungere all'app il completamento della transazione e altre pagine correlate agli ordini.
In questo esercizio, aggiungerai una nuova pagina di completamento della transazione, una barra di navigazione superiore, e userai quindi un componente Blazor NavLink per migliorare il tuo codice.
Clonare l'app esistente del team
Note
In questo modulo vengono usati l'interfaccia della riga di comando di .NET e Visual Studio Code per lo sviluppo locale. Dopo aver completato il modulo, è possibile applicare i concetti usando Visual Studio (Windows) o Visual Studio per Mac (macOS). Per lo sviluppo continuo usare Visual Studio Code per Windows, Linux e macOS.
Questo modulo usa .NET 9.0 SDK. Assicurarsi di avere installato .NET 9.0 eseguendo il comando seguente nel terminale dei comandi preferito:
dotnet --list-sdks
Verrà visualizzato un output simile all'esempio seguente:
8.0.100 [C:\Program Files\dotnet\sdk]
9.0.100 [C:\Program Files\dotnet\sdk]
Assicurarsi che sia elencata una versione che inizia con 9. Se il comando non è presente nell'elenco o non viene trovato, installare la versione più recente di .NET 9.0 SDK.
Se si sta creando la prima app Blazor, seguire le istruzioni di installazione per Blazor per installare la versione corretta di .NET e verificare che il computer sia configurato correttamente. Fermati al passaggio Crea la tua app.
Apri Visual Studio Code.
Aprire il terminale integrato da Visual Studio Code selezionando Visualizza. Quindi nel menu principale selezionare Terminale.
Nel terminale passare alla posizione in cui si vuole creare il progetto.
Clonare l'app da GitHub.
git clone https://github.com/MicrosoftDocs/mslearn-blazor-navigation.git BlazingPizzaSelezionare File e quindi Apri cartella.
Nella finestra di dialogo Apri passare alla cartella BlazingPizza e scegliere Seleziona cartella.
Visual Studio Code potrebbe richiedere informazioni sulle dipendenze non risolte. Selezionare Ripristina.
Eseguire l'app per controllare che tutto funzioni correttamente.
In Visual Studio Code selezionare F5. In alternativa, selezionare Avvia debug nel menu Esegui.
Configurare alcune pizze e aggiungerle all'ordine. Nella parte inferiore della pagina selezionare Order > (Ordina). Viene visualizzato il messaggio predefinito "Spiacenti, non c'è nulla a questo indirizzo." perché non esiste ancora una pagina di checkout.
Per arrestare l'app, premere MAIUSC + F5.
Aggiungere una pagina di completamento della transazione
In Visual Studio Code, in Esplora file, selezionare 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>Il blocco di codice
<NotFound>è quello che visualizzano i clienti se tentano di passare a una pagina che non esiste.In Esplora file espandere Pagine, fare clic con il pulsante destro del mouse sulla cartella e selezionare Nuovo file.
Denominare il nuovo file Checkout.razor. In questo file aggiungere il codice riportato di seguito:
@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; }Questa pagina si basa sull'app corrente e usa lo stato dell'app salvato in
OrderState. Il primodivè la nuova struttura dell'intestazione dell'app. Aggiungerlo alla pagina dell'indice.In Esplora file espandere Pagine e quindi selezionare index.razor.
Sopra la classe
<div class="main">, aggiungere il codice HTMLtop-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>Sarebbe bello mostrare ai clienti quando ci troviamo in questa pagina evidenziando il collegamento. Il team ha già creato una classe CSS
active. Aggiungere quindiactiveall'attributoclassche contiene già lo stilenav-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>In Visual Studio Code selezionare F5. In alternativa, selezionare Avvia debug nel menu Esegui.
L'app ora ha una barra dei menu in alto di aspetto gradevole, che include il logo dell'azienda. Aggiungere alcune pizze e selezionare il pulsante Ordine per passare alla pagina di pagamento. Vengono visualizzati un elenco delle pizze e l'indicatore attivo mancante dal menu.
Per arrestare l'app, premere MAIUSC + F5.
Consentire ai clienti di inserire un ordine
Al momento, la pagina di completamento della transazione non consente ai clienti di effettuare gli ordini. La logica dell'app deve archiviare l'ordine da inviare alla cucina. Dopo l'invio dell'ordine, è necessario reindirizzare i clienti alla home page.
Nella finestra di Esplora file, espandere Pagine e selezionare Checkout.razor.
Modificare l'elemento button con una chiamata al metodo
PlaceOrder. Aggiungere gli attributi@onclickedisabledcome illustrato:<button class="checkout-button btn btn-warning" @onclick="PlaceOrder" disabled=@isSubmitting> Place order </button>Per evitare che i clienti inviino ordini duplicati, si disabilita il pulsante Place order fino a quando l'ordine non viene elaborato.
Nel blocco
@codeaggiungere questo codice sotto il codiceOrder 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("/"); }Il codice precedente disabilita il pulsante Place order (Inserisci ordine), pubblica JSON e lo aggiunge a pizza.db, cancella l'ordine e usa
NavigationManagerper reindirizzare i clienti alla home page.È necessario aggiungere codice per gestire l'ordine. Aggiungere una classe OrderController per questa attività. Se si esamina PizzaStoreContext.cs, è disponibile solo il supporto del database Entity Framework per
PizzaSpecials. Risolvere per prima cosa questo problema.
Aggiungere il supporto di Entity Framework per gli ordini e le pizze
In Esplora file selezionare PizzaStoreContext.cs.
Sostituire la classe
PizzaStoreContextcon il codice seguente: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(); } }Questo codice aggiunge il supporto di Entity Framework per le classi di ordini e pizze dell'app.
Nel menu di Visual Studio Code selezionare File>nuovo file.
Immettere OrderController.cs come nome file. Assicurarsi di salvare il file nella stessa directory di OrderState.cs.
Aggiungere il codice seguente:
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; } }Il codice precedente consente all'app di ottenere tutti gli ordini correnti e inserire un ordine. L'attributo Blazor
[Route("orders")]consente a questa classe di gestire le richieste HTTP in ingresso per /orders e /orders/{orderId}.Salvare le modifiche con CTRL+S.
In l'Esplora file, selezionare OrderState.cs.
Nella parte inferiore della classe del metodo
RemoveConfiguredPizzamodificareResetOrder()per reimpostare l'ordine:public void ResetOrder() { Order = new Order(); }
Testare la funzionalità di completamento della transazione
In Visual Studio Code selezionare F5. In alternativa, selezionare Avvia debug nel menu Esegui.
L'app dovrebbe essere compilata, ma se si crea un ordine e si tenta di completare la transazione, viene visualizzato un errore di runtime. L'errore si verifica perché il database SQLLite pizza.db è stato creato prima del supporto per ordini e pizze. È necessario eliminare il file in modo che sia possibile creare correttamente un nuovo database.
Per arrestare l'app, premere MAIUSC + F5.
In Esplora file eliminare il file pizza.db.
Selezionare F5. In alternativa, selezionare Avvia debug nel menu Esegui.
Fare una prova aggiungendo alcune pizze, accedere alla pagina di completamento della transazione e inserire un ordine. Si viene reindirizzati alla home page dove è possibile notare che l'ordine è ora vuoto.
Per arrestare l'app, premere MAIUSC + F5.
L'app sta migliorando. Abbiamo una configurazione a pizza e un checkout. Ora si vuole consentire ai clienti di visualizzare lo stato del loro ordine dopo averlo inserito.
Aggiungere una pagina degli ordini
In Esplora file espandere Pagine, fare clic con il pulsante destro del mouse sulla cartella e selezionare Nuovo file.
Denominare il nuovo file MyOrders.razor. In questo file aggiungere il codice riportato di seguito:
@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 > </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"); } }È necessario che il riquadro di spostamento cambi in tutte le pagine in modo da includere un collegamento alla nuova pagina My orders (I miei ordini). Aprire Checkout.razor e Index.razor e sostituire il riquadro di spostamento con il codice seguente:
<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>Usando gli elementi
<a>, è possibile gestire manualmente la pagina attiva aggiungendo la classe cssactive. Aggiorniamo tutta la struttura per usare invece un componente NavLink.In tutte e tre le pagine con riquadro di spostamento (Index.razor, Checkout.razor e MyOrders.razor) usare lo stesso codice Blazor per lo spostamento:
<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
activeviene ora aggiunta automaticamente alle pagine dal componente NavLink. Non è necessario ricordare di farlo in ogni pagina in cui è presente la navigazione.L'ultimo passaggio consiste nel modificare
NavigationManagerper reindirizzare alla paginamyordersdopo l'inserimento di un ordine. Nell'esplora file, espandere Pagine e selezionare Checkout.razor.Modificare il metodo
PlaceOrderper reindirizzare alla pagina corretta passando/myordersaNavigationManager.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"); }In Visual Studio Code selezionare F5. In alternativa, selezionare Avvia debug nel menu Esegui.
È possibile ordinare le pizze e quindi visualizzare gli ordini attualmente presenti nel database.
Arrestare l'app premendo MAIUSC + F5.