Exercício - Altere a navegação no seu aplicativo Blazor usando a diretiva @page
Blazor tem um auxiliar de estado de navegação que ajuda o código C# a gerenciar um URI em seu aplicativo. Há também um componente NavLink que substitui o <a> elemento. Um dos recursos do NavLink é adicionar uma classe ativa aos links HTML para os menus de um aplicativo.
Sua equipe começou no aplicativo Blazing Pizza e criou componentes Blazor para representar pizzas e pedidos. Agora, a aplicação precisa adicionar finalização da compra e outras páginas relacionadas ao pedido.
Neste exercício, você adiciona uma nova página de checkout, adiciona uma navegação superior ao aplicativo e usa um componente Blazor NavLink para melhorar seu código.
Clone o aplicativo existente da sua equipe
Note
Este módulo usa a interface de linha de comando (CLI) do .NET e o Visual Studio Code para desenvolvimento local. Depois de concluir este módulo, você pode aplicar os conceitos usando o Visual Studio (Windows) ou o Visual Studio para Mac (macOS). Para desenvolvimento contínuo, use o Visual Studio Code para Windows, Linux e macOS.
Este módulo usa o SDK do .NET 9.0. Certifique-se de ter o .NET 9.0 instalado executando o seguinte comando no seu terminal de comando preferido:
dotnet --list-sdks
Aparece uma saída semelhante ao exemplo seguinte:
8.0.100 [C:\Program Files\dotnet\sdk]
9.0.100 [C:\Program Files\dotnet\sdk]
Certifique-se de que uma versão que começa com 9 está listada. Se nenhum estiver listado ou o comando não for encontrado, instale o SDK do .NET 9.0 mais recente.
Se você estiver criando seu primeiro aplicativo Blazor, siga as instruções de configuração do Blazor para instalar a versão correta do .NET e verifique se sua máquina está configurada corretamente. Pare na etapa de Criar a sua aplicação.
Abra o Visual Studio Code.
Abra o terminal integrado do Visual Studio Code selecionando View. Em seguida, no menu principal, selecione Terminal.
No terminal, vá para onde você deseja que o projeto seja criado.
Clone a aplicação do GitHub.
git clone https://github.com/MicrosoftDocs/mslearn-blazor-navigation.git BlazingPizzaSelecione Arquivo e, em seguida, selecione Abrir pasta.
Na caixa de diálogo Abrir, vá para a pasta BlazingPizza e escolha Selecionar pasta.
O Visual Studio Code pode perguntar sobre dependências não resolvidas. Selecione Restaurar.
Execute o aplicativo para verificar se tudo está funcionando corretamente.
No Visual Studio Code, selecione F5. Ou no menu Executar , selecione Iniciar Depuração.
Configure algumas pizzas e adicione-as ao seu pedido. Selecione Encomendar > na parte inferior da página. A mensagem padrão "Desculpe, não há nada neste endereço." aparece porque ainda não há uma página de checkout.
Para parar o aplicativo, selecione Shift + F5.
Adicionar uma página de finalização de compra
No Visual Studio Code, no explorador 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
<NotFound>bloco de código é o que os clientes veem se tentarem ir para uma página que não existe.No explorador de ficheiros, expanda Páginas, clique com o botão direito do rato na pasta e selecione Novo Ficheiro.
Nomeie o novo arquivo Checkout.razor. Neste ficheiro, 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; }Esta página baseia-se na aplicação atual e utiliza o estado da aplicação guardado no
OrderState. O primeirodivé a nova navegação de cabeçalho do aplicativo. Vamos adicioná-lo à página de índice.No explorador de ficheiros, expanda Páginas e, em seguida, selecione index.razor.
Acima da
<div class="main">classe, adicione otop-barhtml.<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 estamos nesta página, seria bom destacar o link para mostrar aos clientes. A equipe já criou uma
activeclasse css, então adicioneactiveaoclassatributo que já contém onav-tabestilo.<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 no menu Executar , selecione Iniciar Depuração.
O aplicativo agora tem uma bela barra de menu na parte superior, que inclui o logotipo da empresa. Adicione algumas pizzas e selecione o botão Encomendar para avançar para a página de checkout. Você vê as pizzas listadas e nota que o indicador ativo está ausente do menu.
Para parar o aplicativo, selecione Shift + F5.
Permitir que os clientes façam um pedido
No momento, a página de checkout não permite que os clientes façam seus pedidos. A lógica do aplicativo precisa armazenar o pedido para enviar para a cozinha. Depois que o pedido for enviado, vamos redirecionar os clientes de volta para a página inicial.
No explorador de ficheiros, expanda Páginas e selecione Checkout.razor.
Modifique o elemento button com uma chamada para o método
PlaceOrder. Adicione os@onclickatributos edisabledconforme mostrado:<button class="checkout-button btn btn-warning" @onclick="PlaceOrder" disabled=@isSubmitting> Place order </button>Não queremos que os clientes façam pedidos duplicados, por isso desativamos o botão Fazer pedido até que o pedido seja processado.
No bloco
@code, adicione este código debaixo doOrder 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 desativa o botão Fazer pedido , lança JSON e o adiciona ao pizza.db, limpa o pedido e usa
NavigationManagerpara redirecionar os clientes para a página inicial.Você precisa adicionar código para lidar com o pedido. Adicione uma classe OrderController para esta tarefa. Se examinares PizzaStoreContext.cs, verás apenas o suporte ao banco de dados do Entity Framework para
PizzaSpecials. Vamos corrigir isso primeiro.
Adicionar suporte do Entity Framework para pedidos e pizzas
No explorador de ficheiros, selecione PizzaStoreContext.cs.
Substitua a
PizzaStoreContextclasse 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 à estrutura de entidade para as classes de pedido e pizza do aplicativo.
No Visual Studio Code, no menu, selecione Arquivo>Novo Arquivo.
Digite OrderController.cs como nome do arquivo. Certifique-se de salvar o arquivo no mesmo diretório que OrderState.cs.
Adicione o seguinte 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
[Route("orders")]atributo Blazor permite que essa classe manipule solicitações HTTP de entrada para /orders e /orders/{orderId}.Salve suas alterações com Ctrl+S.
No explorador de ficheiros, selecione OrderState.cs.
Na parte inferior da classe sob o
RemoveConfiguredPizzamétodo, modifiqueResetOrder()para redefinir a ordem:public void ResetOrder() { Order = new Order(); }
Testar a funcionalidade de finalização de compras
No Visual Studio Code, selecione F5. Ou no menu Executar , selecione Iniciar Depuração.
A aplicação deve compilar, mas se criares uma encomenda e tentares finalizar a compra, verás um erro de tempo de execução. O erro ocorre porque nosso banco de dados SQLLite pizza.db foi criado antes de haver suporte para pedidos e pizzas. Precisamos excluir o arquivo para que um novo banco de dados possa ser criado corretamente.
Para parar o aplicativo, selecione Shift + F5.
No explorador de ficheiros, elimine o ficheiro pizza.db .
Selecione F5. Ou no menu Executar , selecione Iniciar Depuração.
Como teste, adicione pizzas, vá ao checkout e faça um pedido. Você é redirecionado para a página inicial e pode ver que o pedido agora está vazio.
Para parar o aplicativo, selecione Shift + F5.
O aplicativo está melhorando. Temos personalização de pizza e finalização da compra. Queremos permitir que os clientes vejam o status de seu pedido de pizza depois de colocá-lo.
Adicionar uma página de encomendas
No explorador de ficheiros, expanda Páginas, clique com o botão direito do rato na pasta e selecione Novo Ficheiro.
Nomeie o novo arquivo MyOrders.razor. Neste ficheiro, 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 mudar em todas as páginas que temos agora para 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>Ao usar elementos
<a>, podemos controlar qual é a página ativa manualmente adicionando a classe CSSactive. Vamos atualizar toda a navegação para usar um componente NavLink .Em todas as três páginas com navegação (Index.razor, Checkout.razor e MyOrders.razor), use o mesmo código Blazor para 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
activeclasse css agora é adicionada automaticamente às páginas pelo componente NavLink . Você não precisa se lembrar de fazê-lo em cada página na qual há navegação.O último passo é alterar
NavigationManagerpara redirecionar para amyorderspágina depois que um pedido é feito. No explorador de ficheiros, expanda Páginas e, em seguida, selecione Checkout.razor.Altere o
PlaceOrdermétodo para redirecionar para a página correta passando/myordersparaNavigationManager.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 no menu Executar , selecione Iniciar Depuração.
Você deve ser capaz de pedir algumas pizzas e, em seguida, ver os pedidos atualmente no banco de dados.
Pare o aplicativo selecionando Shift + F5.