Ejercicio: Cambio de la navegación en la aplicación Blazor mediante la directiva @page

Completado

Blazor tiene un asistente de estado de navegación que ayuda al código C# a administrar los URI de una aplicación. También hay un componente NavLink que sustituye al elemento <a>. Una de las características de NavLink es agregar una clase activa a vínculos HTML para los menús de una aplicación.

El equipo ha hecho un inicio en la aplicación Blazing Pizza y ha compilado componentes Blazor para representar pizzas y pedidos. Ahora la aplicación debe agregar la desprotección y otras páginas relacionadas con el pedido.

En este ejercicio, agregará una nueva página de desprotección, agregará una navegación superior a la aplicación y, a continuación, usará un componente blazor NavLink para mejorar el código.

Clonación de la aplicación existente del equipo

Nota:

En este módulo se usan la interfaz de la línea de comandos (CLI) de .NET y Visual Studio Code para el desarrollo local. Después de finalizar este módulo, puede aplicar los conceptos mediante Visual Studio (Windows) o Visual Studio para Mac (macOS). Para un desarrollo continuo, use Visual Studio Code para Windows, Linux y macOS.

En este módulo se usa el SDK de .NET 6.0. Asegúrese de que tiene instalado .NET 6.0 mediante la ejecución del siguiente comando en la terminal que prefiera:

dotnet --list-sdks

Verá un resultado similar al siguiente:

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

Asegúrese de que aparezca una versión que comience en 6. Si no aparece ninguna o no se encuentra el comando, instale el SDK más reciente de .NET 6.0.

Si no ha creado antes una aplicación de Blazor, siga las instrucciones de instalación de Blazor para instalar la versión correcta de .NET y compruebe que la máquina esté configurada correctamente. Deténgase en el paso Create your app (Creación de la aplicación).

  1. Abra Visual Studio Code.

  2. Seleccione Ver para abrir el terminal integrado desde Visual Studio Code. Después, en el menú principal, seleccione Terminal.

  3. En el terminal, vaya a donde quiera crear el proyecto.

  4. Clone la aplicación desde GitHub.

    git clone https://github.com/MicrosoftDocs/mslearn-blazor-navigation.git BlazingPizza
    
  5. Seleccione Archivo y luego Abrir carpeta.

  6. En el cuadro de diálogo Abrir, vaya a la carpeta BlazingPizza y seleccione Seleccionar carpeta.

    Es posible que Visual Studio Code le pregunte sobre las dependencias sin resolver. Seleccione Restaurar.

  7. Ejecute la aplicación para comprobar que todo funcione correctamente.

  8. En Visual Studio Code, presione F5. O bien, en el menú Ejecutar, seleccione Iniciar depuración.

    Captura de pantalla que muestra la versión clonada de la aplicación Blazing Pizza.

    Configure algunas pizzas y agréguelas al pedido. Seleccione Order> (Pedir) al final de la página. Aparece el mensaje predeterminado "404 no encontrado" porque aún no hay una página de desprotección.

  9. Para detener la aplicación, seleccione Mayús + F5.

Adición de una página de finalización de la compra

  1. En el explorador de archivos de Visual Studio Code, seleccione 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>
    

    El bloque de código <NotFound> es lo que los clientes ven si intentan ir a una página que no existe.

  2. En el explorador de archivos, expanda Páginas, haga clic con el botón derecho en la carpeta y seleccione Nuevo archivo.

  3. Asigne el nombre Checkout.razor al nuevo archivo. En este archivo, escriba el código siguiente:

    @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 se basa en la aplicación actual y usa el estado de la aplicación guardado en OrderState. El primer elemento div es la nueva navegación de encabezado de la aplicación. Vamos a agregarlo a la página de índice.

  4. En el explorador de archivos, expanda Páginas y, después, seleccione index.razor.

  5. Por encima de la clase <div class="main">, agregue el 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>
    

    Cuando esté en esta página, sería bueno indicárselo a los clientes resaltando el vínculo. El equipo ya creó una activeclase css, por lo que debe agregar active al atributo class que ya contiene el estilo 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. En Visual Studio Code, presione F5. O bien, en el menú Ejecutar, seleccione Iniciar depuración.

    Ahora la aplicación tiene una bonita barra de menús en la parte superior que incluye el logotipo de la empresa. Agregue algunas pizzas y avance el pedido a la página de finalización de la compra. Verá las pizzas enumeradas y el indicador activo que falta en el menú.

    Captura de pantalla en la que se muestra la página de finalización de la compra con algunas pizzas.

  7. Para detener la aplicación, seleccione Mayús + F5.

Permitir a los clientes realizar un pedido

En este momento, la página de finalización de la compra no permite a los clientes realizar sus pedidos. La lógica de la aplicación debe almacenar el pedido para enviarlo a la cocina. Una vez que se envía el pedido, se redirige los clientes de nuevo a la página principal.

  1. En el explorador de archivos, expanda Páginas y, después, seleccione Checkout.razor.

  2. Modifique el elemento de botón con una llamada al método PlaceOrder. Agregue los atributos @onclick y disabled como se muestra a continuación:

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

    No quiere que los clientes realicen pedidos duplicados, por lo que deshabilitará el botón Hacer pedido hasta que se procese el pedido.

  3. En el bloque @code, agregue este código debajo de código 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("/");
    }
    

    El código anterior deshabilita el botón Realizar pedido, publica JSON y lo agrega a pizza.db, borra el pedido y usa NavigationManager para redirigir a los clientes a la página principal.

    Debe agregar código para controlar el pedido. Agregue una clase OrderController para esta tarea. Si examina PizzaStoreContext.cs, solo verá compatibilidad con la base de datos de Entity Framework para PizzaSpecials. Vamos a corregir esto primero.

Adición de compatibilidad con Entity Framework para pedidos y pizzas

  1. En el explorador de archivos, seleccione PizzaStoreContext.cs.

  2. Reemplace la clase 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();
            }
    
      }
    

    Este código agrega compatibilidad con Entity Framework para las clases de pedido y pizza de la aplicación.

  3. En el menú de Visual Studio Code, seleccione Archivo>Nuevo archivo de texto.

  4. Seleccione el lenguaje C# y escriba 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;
        }
    }
    

    El código anterior permite a la aplicación obtener todos los pedidos actuales y hacer un pedido. El atributo de Blazor [Route("orders")] permite que esta clase controle las solicitudes HTTP entrantes para /orders y /orders/{orderId}.

  5. Presione Ctrl+S para guardar los cambios.

  6. Para el nombre de archivo, use OrderController.cs. Asegúrese de guardar el archivo en el mismo directorio que OrderState.cs.

  7. En el explorador de archivos, seleccione OrderState.cs.

  8. En la parte inferior de la clase en el método RemoveConfiguredPizza, modifique ResetOrder() para restablecer el pedido:

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

Prueba de la funcionalidad de finalización de la compra

  1. En Visual Studio Code, presione F5. O bien, en el menú Ejecutar, seleccione Iniciar depuración.

    La aplicación debe compilarse, pero si crea un pedido e intenta des check out, verá un error en tiempo de ejecución. Esto se debe a que la base de datos SQLLite pizza.db se ha creado antes de que se admitieran los pedidos y las pizzas. Es necesario eliminar el archivo para que se pueda crear correctamente una base de datos.

  2. Para detener la aplicación, seleccione Mayús + F5.

  3. En el explorador de archivos, elimine el archivo pizza.db.

  4. Seleccione F5. O bien, en el menú Ejecutar, seleccione Iniciar depuración.

    Como prueba, agregue pizzas, vaya a la finalización de la compra y haga un pedido. Se le redirige a la página principal y puede ver que el pedido ahora está vacío.

  5. Para detener la aplicación, seleccione Mayús + F5.

La aplicación está mejorando. Tenemos una configuración de pizza y una finalización de la compra. El objetivo es permitir que los clientes vean el estado de su pedido de pizza después de realizarlo.

Adición de una página de pedidos

  1. En el explorador de archivos, expanda Páginas, haga clic con el botón derecho en la carpeta y seleccione Nuevo archivo.

  2. Asigne el nombre MyOrders.razor al nuevo archivo. En este archivo, escriba el código siguiente:

    @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 navegación debe cambiar en todas las páginas actuales para incluir un vínculo a la nueva página Mis pedidos. Abra Checkout.razor e Index.razor, y reemplace la navegación 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>
    

    Mediante el uso de elementos <a>, se puede administrar cuál es la página activa manualmente si se agrega la clase active de CSS. Vamos a actualizar toda la navegación para usar un componente NavLink en su lugar.

  3. En las tres páginas con navegación (Index.razor, Checkout.razor y MyOrders.razor), use el mismo código de Blazor para la navegación:

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

    Ahora la clase active de CSS se agrega automáticamente a las páginas mediante el componente NavLink. No tiene que recordar hacerlo en cada página en la que se encuentra la navegación.

  4. El último paso es cambiar NavigationManager para redirigir a la página myorders después de realizar un pedido. En el explorador de archivos, expanda Páginas y, después, seleccione Checkout.razor.

  5. Cambie el método PlaceOrder para redirigir a la página correcta pasando /myorders a 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. En Visual Studio Code, presione F5. O bien, en el menú Ejecutar, seleccione Iniciar depuración.

    Captura de pantalla que muestra la página del pedido.

    Debería poder pedir algunas pizzas y, a continuación, ver los pedidos que están actualmente en la base de datos.

  7. Para detener la aplicación, seleccione Mayús + F5.