Exercício - Altere a navegação no seu aplicativo Blazor usando a diretiva @page

Concluído

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.

  1. Abra o Visual Studio Code.

  2. Abra o terminal integrado do Visual Studio Code selecionando View. Em seguida, no menu principal, selecione Terminal.

  3. No terminal, vá para onde você deseja que o projeto seja criado.

  4. Clone a aplicação do GitHub.

    git clone https://github.com/MicrosoftDocs/mslearn-blazor-navigation.git BlazingPizza
    
  5. Selecione Arquivo e, em seguida, selecione Abrir pasta.

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

  7. Execute o aplicativo para verificar se tudo está funcionando corretamente.

  8. No Visual Studio Code, selecione F5. Ou no menu Executar , selecione Iniciar Depuração.

    Captura de tela mostrando a versão clonada do aplicativo Blazing Pizza.

    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.

  9. Para parar o aplicativo, selecione Shift + F5.

Adicionar uma página de finalização de compra

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

  2. No explorador de ficheiros, expanda Páginas, clique com o botão direito do rato na pasta e selecione Novo Ficheiro.

  3. 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 primeiro div é a nova navegação de cabeçalho do aplicativo. Vamos adicioná-lo à página de índice.

  4. No explorador de ficheiros, expanda Páginas e, em seguida, selecione index.razor.

  5. Acima da <div class="main"> classe, adicione o top-bar html.

    <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 active classe css, então adicione active ao class atributo que já contém o nav-tab estilo.

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

    Captura de tela mostrando a página de checkout com algumas pizzas.

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

  1. No explorador de ficheiros, expanda Páginas e selecione Checkout.razor.

  2. Modifique o elemento button com uma chamada para o método PlaceOrder. Adicione os @onclick atributos e disabled conforme 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.

  3. No bloco @code, adicione este código debaixo do 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("/");
    }
    

    O código anterior desativa o botão Fazer pedido , lança JSON e o adiciona ao pizza.db, limpa o pedido e usa NavigationManager para 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

  1. No explorador de ficheiros, selecione PizzaStoreContext.cs.

  2. Substitua a PizzaStoreContext classe 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.

  3. No Visual Studio Code, no menu, selecione Arquivo>Novo Arquivo.

  4. Digite OrderController.cs como nome do arquivo. Certifique-se de salvar o arquivo no mesmo diretório que OrderState.cs.

  5. 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}.

  6. Salve suas alterações com Ctrl+S.

  7. No explorador de ficheiros, selecione OrderState.cs.

  8. Na parte inferior da classe sob o RemoveConfiguredPizza método, modifique ResetOrder() para redefinir a ordem:

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

Testar a funcionalidade de finalização de compras

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

  2. Para parar o aplicativo, selecione Shift + F5.

  3. No explorador de ficheiros, elimine o ficheiro pizza.db .

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

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

  1. No explorador de ficheiros, expanda Páginas, clique com o botão direito do rato na pasta e selecione Novo Ficheiro.

  2. 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 &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");
        }
    }
    

    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 CSS active. Vamos atualizar toda a navegação para usar um componente NavLink .

  3. 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 active classe 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.

  4. O último passo é alterar NavigationManager para redirecionar para a myorders página depois que um pedido é feito. No explorador de ficheiros, expanda Páginas e, em seguida, selecione Checkout.razor.

  5. Altere o PlaceOrder método para redirecionar para a página correta passando /myorders para 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. No Visual Studio Code, selecione F5. Ou no menu Executar , selecione Iniciar Depuração.

    Captura de ecrã a mostrar a página de encomenda.

    Você deve ser capaz de pedir algumas pizzas e, em seguida, ver os pedidos atualmente no banco de dados.

  7. Pare o aplicativo selecionando Shift + F5.