Упражнение. Изменение навигации в приложении Blazor с помощью директивы @Page
Blazor имеет помощник по состоянию навигации, который помогает коду C# управлять URI в вашем приложении. Существует также компонент NavLink, который представляет собой замену для элемента <a>. Одна из функций NavLink — добавление активного класса в ссылки HTML для меню приложения.
Ваша команда начала работу с приложением Blazing Pizza и построила компоненты Blazor для представления пиццы и заказов. Теперь приложению необходимо добавить страницу оформления заказа и другие страницы, связанные с заказом.
В этом упражнении вы добавите новую страницу оформления заказа, добавите верхнюю навигацию в приложение, а затем используете компонент Blazor NavLink для улучшения кода.
Клонируйте существующее приложение вашей команды.
Note
В этом модуле используются .NET CLI и Visual Studio Code для локальной разработки. После завершения этого модуля вы сможете применять основные понятия с помощью Visual Studio (Windows) или Visual Studio для Mac (macOS). Для продолжения разработки используйте Visual Studio Code для Windows, Linux и macOS.
Этот модуль использует пакет SDK для .NET 9.0. Убедитесь, что установлен .NET 9.0, выполнив следующую команду в предпочтительном терминале команд:
dotnet --list-sdks
Выходные данные, аналогичные следующему примеру, отображаются:
8.0.100 [C:\Program Files\dotnet\sdk]
9.0.100 [C:\Program Files\dotnet\sdk]
Убедитесь, что в списке есть версия, которая начинается с цифры 9. Если ни один из них отсутствует или команда не найдена, установите последний пакет SDK для .NET 9.0.
Если вы создаете первое приложение Blazor, следуйте инструкциям по настройке Blazor, чтобы установить правильную версию .NET и проверить правильность настройки компьютера. Остановитесь на шаге Create your app (Создание приложения).
Откройте Visual Studio Code.
Откройте интегрированный терминал в Visual Studio Code, выбрав Вид. Затем в главном меню выберите Терминал.
В терминале перейдите туда, где вы хотите создать проект.
Клонируйте приложение из GitHub.
git clone https://github.com/MicrosoftDocs/mslearn-blazor-navigation.git BlazingPizzaВыберите Файл, а затем — Открыть папку.
В диалоговом окне Открыть перейдите в папку BlazingPizza и щелкните Выбрать папку.
Visual Studio Code может сообщить о неразрешенных зависимостях. Выберите Восстановить.
Запустите приложение, чтобы проверить, что все работает правильно.
В Visual Studio Code выберите F5. Выберите Начать отладку в меню Выполнить.
Настройте несколько пицц и добавьте их в свой заказ. В нижней части страницы нажмите кнопку Заказать>. Сообщение по умолчанию "К сожалению, на этом адресе нет ничего" появляется, потому что страница оформления заказа ещё не существует.
Чтобы остановить приложение, нажмите клавиши SHIFT + F5.
Добавление страницы оформления заказа
В Visual Studio Code в проводнике выберите 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>Блок
<NotFound>кода — это то, что клиенты видят, если они пытаются перейти на страницу, которая не существует.В проводнике разверните элемент Pages, щелкните правой кнопкой мыши папку и выберите Создать файл.
Присвойте новому файлу имя Checkout.razor. Добавьте в этот файл следующий код:
@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; }Эта страница построена на основе текущего приложения и использует состояние приложения, сохраненное в
OrderState. Первыйdiv— это новая навигация в заголовке приложения. Добавим его на страницу индекса.В проводнике разверните Страницы и выберите index.razor.
Над классом
<div class="main">добавьте 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>Когда мы на этой странице, было бы неплохо показать клиентам, выделив ссылку. Команда уже создала
activeкласс css, поэтому добавьтеactiveвclassатрибут, который уже содержит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>В Visual Studio Code выберите F5. Выберите Начать отладку в меню Выполнить.
Теперь в верхней части приложения есть отличная строка меню, а также логотип компании. Добавьте пиццы и нажмите кнопку "Заказ", чтобы открыть страницу оформления заказа. Вы увидите перечисленные пиццы и активный индикатор, отсутствующий в меню.
Чтобы остановить приложение, нажмите клавиши SHIFT + F5.
Предоставление клиентам возможности оформлять заказ
Сейчас клиенты не могут оформлять заказы на странице оформления заказа. Логика приложения должна хранить заказ для отправки на кухню. Давайте перенаправим клиентов обратно на домашнюю страницу после отправки заказа.
В файловом менеджере разверните Страницы и выберите Checkout.razor.
Измените элемент кнопки вызовом метода
PlaceOrder. Добавьте атрибуты@onclickиdisabled, как показано ниже:<button class="checkout-button btn btn-warning" @onclick="PlaceOrder" disabled=@isSubmitting> Place order </button>Мы не хотим, чтобы клиенты оформляли повторяющиеся заказы, поэтому кнопка Place order (Разместить заказ) будет отключена, пока заказ не будет обработан.
В блоке
@codeдобавьте этот код под кодом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("/"); }Приведенный выше код отключает кнопку "Заказать", публикует JSON и добавляет его в pizza.db, очищает заказ и использует для перенаправления клиентов на домашнюю страницу.
Чтобы обработать заказ, необходимо добавить код. Добавьте класс OrderController для этой задачи. Если вы посмотрите на PizzaStoreContext.cs, вы увидите поддержку
PizzaSpecialsтолько для базы данных Entity Framework. Давайте сначала это исправим.
Добавить поддержку Entity Framework для заказов и пиццы
В проводнике выберите файл PizzaStoreContext.cs.
Замените класс
PizzaStoreContextэтим кодом: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(); } }Этот код добавляет поддержку Entity Framework для классов Order и Pizza приложения.
В Visual Studio Code в меню выберите "Файл>Новый файл".
Введите OrderController.cs в качестве имени файла. Убедитесь, что файл сохранен в том же каталоге, что и OrderState.cs.
Добавьте следующий код:
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; } }С помощью приведенного выше кода наше приложение может получить все текущие заказы и разместить заказ. Благодаря атрибуту Blazor
[Route("orders")]этот класс может обрабатывать входящие HTTP-запросы для /orders и /orders/{orderId}.Сохраните изменения, нажав клавиши CTRL+S.
В проводнике выберите файл OrderState.cs.
В нижней части класса в методе
RemoveConfiguredPizzaизменитеResetOrder()для сброса порядка:public void ResetOrder() { Order = new Order(); }
Проверка функции оформления заказа
В Visual Studio Code выберите F5. Выберите Начать отладку в меню Выполнить.
Приложение должно скомпилироваться, но если вы создаете заказ и пытаетесь оформить его, вы увидите ошибку среды выполнения. Ошибка связана с тем, что база данных SQLLite pizza.db создана до того, как появилась поддержка заказов и пицц. Необходимо удалить файл, чтобы правильно создать базу данных.
Чтобы остановить приложение, нажмите клавиши SHIFT + F5.
В проводнике удалите файл pizza.db.
Нажмите клавишу F5. Выберите Начать отладку в меню Выполнить.
В качестве теста добавьте пиццы, перейдите к оформлению заказа и отправьте заказ. Вы перенаправляетесь на домашнюю страницу, и вы увидите, что заказ теперь пуст.
Чтобы остановить приложение, нажмите клавиши SHIFT + F5.
Это приложение становится совершеннее. У нас есть настройка пиццы и оформление заказа. Мы хотим позволить клиентам видеть состояние размещенного заказа на пиццу после оформления.
Добавление страницы заказов
В проводнике разверните элемент Pages, щелкните правой кнопкой мыши папку и выберите Создать файл.
Присвойте новому файлу имя MyOrders.razor. Добавьте в этот файл следующий код:
@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"); } }Необходимо изменить навигацию на всех имеющихся страницах, чтобы добавить ссылку на новую страницу My orders (Мои заказы). Откройте файл Checkout.razor и Index.razor и замените навигацию следующим кодом:
<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>С помощью элементов
<a>можно вручную управлять активной страницей, добавив класс CSSactive. Давайте обновим всю навигацию, чтобы использовать компонент NavLink.На всех трех страницах с навигацией (Index.razor, Checkout.razor и MyOrders.razor) используйте один и тот же код Blazor для навигации:
<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>Теперь класс CSS
activeавтоматически добавлен на страницы с помощью компонента NavLink. Вам не нужно выполнять эту операцию на каждой странице с включенной навигацией.Последним шагом является изменение
NavigationManager, чтобы перенаправлять наmyordersстраницу после размещения заказа. В проводнике разверните элемент Страницы, затем выберите Checkout.razor.Измените метод
PlaceOrderдля перенаправления на правильную страницу, передав/myordersв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"); }В Visual Studio Code выберите F5. Выберите Начать отладку в меню Выполнить.
Теперь вы можете заказать пиццы, а затем просмотреть заказы в базе данных.
Остановите приложение, нажав клавиши SHIFT + F5.