Exercício – Alterar a navegação no aplicativo Blazor usando a diretiva @page
O Blazor tem um auxiliar de estado de navegação que ajuda o código C# a gerenciar URIs de um aplicativo. Há também um componente NavLink que é uma substituição drop-in do elemento <a>
. Um dos recursos do NavLink é adicionar uma classe ativa a links HTML para os menus de um aplicativo.
Sua equipe começou a trabalhar no aplicativo Blazing Pizza e criou componentes Blazor para representar pizzas e pedidos. Agora o aplicativo precisa adicionar páginas de checkout e outras páginas relacionadas a pedidos.
Nesse exercício, você adiciona uma nova página de checkout, adiciona uma navegação superior ao aplicativo e, em seguida, usa um componente Blazor NavLink para melhorar seu código.
Clonar o aplicativo existente de sua equipe
Observação
Este módulo usa a CLI (interface de linha de comando) do .NET e o Visual Studio Code para o desenvolvimento local. Depois de concluir este módulo, você poderá aplicar os conceitos usando o Visual Studio (Windows) ou o Visual Studio para Mac (macOS). Para o desenvolvimento contínuo, use o Visual Studio Code para Windows, Linux e macOS.
Este módulo usa o SDK do .NET 6.0. Verifique se tem o .NET 6.0 instalado, executando o seguinte comando em seu terminal preferido:
dotnet --list-sdks
Saída semelhante à seguinte exibida:
3.1.100 [C:\program files\dotnet\sdk]
5.0.100 [C:\program files\dotnet\sdk]
6.0.100 [C:\program files\dotnet\sdk]
Verifique se uma versão que começa com 6
está listada. Se nenhum estiver listado ou o comando não for encontrado, instale o SDK do .NET 6.0 mais recente.
Se você ainda não criou um aplicativo Blazor, siga as instruções de instalação do Blazor para instalar a versão correta do .NET e verificar se o computador está configurado corretamente. Pare na etapa Criar seu aplicativo.
Abra o Visual Studio Code.
Abra o terminal integrado no Visual Studio Code selecionando Exibir. Em seguida, no menu principal, escolha Terminal.
No terminal, acesse o local em que deseja criar o projeto.
Clone o aplicativo do GitHub.
git clone https://github.com/MicrosoftDocs/mslearn-blazor-navigation.git BlazingPizza
Selecione Arquivo e Abrir pasta.
Na caixa de diálogo Abrir, acesse a pasta BlazingPizza e escolha Selecionar Pasta.
O Visual Studio Code pode solicitar as dependências não resolvidas. Selecione Restaurar.
Execute o aplicativo para verificar se tudo está funcionando corretamente.
No Visual Studio Code, selecione F5. Ou, então, no menu Executar, selecione Iniciar Depuração.
Configure algumas pizzas e adicione-as ao seu pedido. Selecione Pedido > na parte inferior da página. A mensagem padrão "404 não encontrado" aparece porque ainda não há uma página de checkout.
Para parar o aplicativo, selecione Shift + F5.
Adicionar uma página de check-out
No Visual Studio Code, no gerenciador de arquivos, selecione 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>
O bloco de código
<NotFound>
é o que os clientes veem se tentarem acessar uma página que não existe.No explorador de arquivos, expanda Páginas, clique com o botão direito do mouse na pasta e escolha Novo Arquivo.
Dê ao novo arquivo o nome Checkout.razor. Nesse arquivo, escreva o seguinte código:
@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; }
Essa página é criada no aplicativo atual e usa o estado do aplicativo salvo em
OrderState
. O primeirodiv
é a nova navegação de cabeçalho do aplicativo. Vamos adicioná-lo à página de índice.No explorador de arquivos, expanda Páginas e selecione index.razor.
Acima da classe
<div class="main">
, adicione o 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>
Quando estivermos nessa página, será interessante mostrar isso aos clientes realçando o link. A equipe já criou uma classe
active
css, então adicioneactive
ao atributoclass
que já contém o estilonav-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>
No Visual Studio Code, selecione F5. Ou, então, no menu Executar, selecione Iniciar Depuração.
O aplicativo agora tem uma barra de menus interessante na parte superior, que inclui o logotipo da empresa. Adicione algumas pizzas e avance o pedido até a página de check-out. Você vê as pizzas listadas e o indicador ativo faltando no menu.
Para parar o aplicativo, selecione Shift + F5.
Permitir que os clientes façam um pedido
No momento, a página de check-out não permite que os clientes façam pedidos. A lógica do aplicativo precisa armazenar o pedido a fim de enviá-lo para a cozinha. Depois que o pedido é enviado, vamos redirecionar os clientes novamente para a home page.
No explorador de arquivos, expanda Páginas e selecione Checkout.razor.
Modifique o elemento do botão com uma chamada ao método
PlaceOrder
. Adicione os atributos@onclick
edisabled
, conforme mostrado abaixo:<button class="checkout-button btn btn-warning" @onclick="PlaceOrder" disabled=@isSubmitting> Place order </button>
Para que os clientes não façam pedidos duplicados, o botão Fazer um pedido foi desabilitado até a conclusão do processamento do pedido.
No bloco
@code
, adicione este código abaixo do códigoOrder 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("/"); }
O código anterior desabilita o botão Fazer pedido, publica JSON e o adiciona a pizza.db, limpa o pedido e usa
NavigationManager
para redirecionar os clientes para a página inicial.Você precisa adicionar o código para tratar da ordem. Adicione uma classe OrderController para essa tarefa. Se você olhar para PizzaStoreContext.cs, verá apenas suporte ao banco de dados do entity framework para
PizzaSpecials
. Vamos corrigir isso primeiro.
Adicionar suporte do Entity Framework a pedidos e pizzas
No explorador de arquivos, selecione PizzaStoreContext.cs.
Substitua a classe
PizzaStoreContext
por este código: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(); } }
Esse código adiciona suporte do Entity Framework às classes order e pizza do aplicativo.
No menu do Visual Studio Code, escolha Arquivo>Novo arquivo de texto.
Selecione a linguagem C# e insira este código:
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; } }
O código anterior permite que nosso aplicativo obtenha todos os pedidos atuais e faça um pedido. O atributo
[Route("orders")]
do Blazor permite que essa classe manipule solicitações HTTP de entrada para /orders e /orders/{orderId}.Salve as alterações com Ctrl+S.
Como nome de arquivo, use OrderController.cs. Salve o arquivo no mesmo diretório como OrderState.cs.
No explorador de arquivos, selecione OrderState.cs.
Na parte inferior da classe no método
RemoveConfiguredPizza
, modifiqueResetOrder()
para redefinir a ordem:public void ResetOrder() { Order = new Order(); }
Testar a funcionalidade de check-out
No Visual Studio Code, selecione F5. Ou, então, no menu Executar, selecione Iniciar Depuração.
O aplicativo deve compilar, mas se você criar um pedido e tentar finalizar a compra, verá um erro de tempo de execução. O erro ocorre porque o banco de dados SQLLite pizza.db foi criado antes do suporte para pedidos e pizzas. Precisamos excluir o arquivo para que um banco de dados possa ser criado corretamente.
Para parar o aplicativo, selecione Shift + F5.
No explorador de arquivos, exclua o arquivo pizza.db.
Selecione F5. Ou, então, no menu Executar, selecione Iniciar Depuração.
Como um teste, adicione pizzas, vá para o check-out e faça um pedido. Você será redirecionado para a página inicial e poderá ver que o pedido agora está vazio.
Para parar o aplicativo, selecione Shift + F5.
O aplicativo está melhorando. Temos a configuração de uma pizza e um check-out. Queremos permitir que os clientes vejam o status do pedido de pizza após o pedido.
Adicionar uma página de pedidos
No explorador de arquivos, expanda Páginas, clique com o botão direito do mouse na pasta e escolha Novo Arquivo.
Dê ao novo arquivo o nome MyOrders.razor. Nesse arquivo, escreva o seguinte código:
@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"); } }
A navegação precisa ser alterada em todas as páginas. Agora precisamos incluir um link para a nova página Meus pedidos. Abra Checkout.razor e Index.razor e substitua a navegação por este código:
<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 elementos
<a>
, precisamos gerenciar qual é a página ativa manualmente adicionando a classe CSSactive
. Vamos atualizar toda a navegação para usar um componente NavLink.Nas três páginas com navegação (Index.razor, Checkout.razor e MyOrders.razor), use o mesmo código do Blazor para a navegação:
<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>
A classe CSS
active
agora é adicionada automaticamente às páginas pelo componente NavLink. Você não precisa se lembrar de fazer isso em cada página em que há navegação.O último passo é alterar
NavigationManager
para redirecionar para a páginamyorders
após um pedido ser feito. No explorador de arquivos, expanda Páginas e selecione Checkout.razor.Altere o método
PlaceOrder
a fim de redirecionar para a página correta transmitindo/myorders
paraNavigationManager.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"); }
No Visual Studio Code, selecione F5. Ou, então, no menu Executar, selecione Iniciar Depuração.
Você deve conseguir pedir algumas pizzas e ver os pedidos atualmente no banco de dados.
Pare o aplicativo selecionando Shift + F5.