Содержание
Маршрутизация в Blazor — это процесс сопоставление запроса с конкретным адресом внутри приложения. За выполнение маршрутизации к компонентам Razor в приложении Blazor Hybrid отвечает специальный компонент Router
, который, в свою очередь, размещается в компоненте Components/Routes.razor
.
Общие сведения о маршрутизации в Blazor
Компонент Router
обеспечивает маршрутизацию к компонентам Razor и находится в компоненте Routes
приложения. Например, по умолчанию этот компонент выглядит следующим образом:
<Router AppAssembly="@typeof(MauiProgram).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> </Router>
Если компонент Razor содержит директиву @page
, то при компиляции, сгенерированному классу компонента присваивается специальный атрибут RouteAttribute
, определяющий шаблон маршрута компонента.
При запуске приложения сборка, указанная в параметре AppAssembly
маршрутизатора, сканируется для сбора информации о маршрутах для компонентов приложения, с установленным RouteAttribute
.
Во время выполнения компонент RouteView
(см. код выше):
- Получает специальный объект
RouteData
, в котором содержится вся необходимая информация о маршруте - Отображает указанный компонент.
При необходимости, мы можем настроить другие свойства и параметры компонента Router
в нашем приложении.
Компонент Router
Компонент Router
предоставляет следующие свойства:
Свойство | Тип | Описание |
AppAssembly |
Assembly |
Сборка, которая должна сканироваться для сбора информации о маршрутах |
AdditionalAssemblies |
IEnumerable<Assembly> |
Коллекция дополнительных сборок для сканирования информации о маршрутах |
Found |
RenderFragment<RouteData> |
Содержимое, которое должно отображаться в случае обнаружения подходящего запросу пользователя маршрута |
Navigating |
RenderFragment |
Содержимое, которое должно отображаться в момент выполнения асинхронной навигации по установленному маршруту |
NotFound |
RenderFragment |
Содержимое, которое должно отображаться в случае, если подходящий запросу пользователя маршрут не установлен |
OnNavigateAsync |
EventCallback<NavigationContext> |
Метод обратного вызова, который должен выполняться перед переходом на новую страницу приложения |
Чтобы продемонстрировать действие этих свойств, создадим новое приложение Blazor Hybrid, перейдем к компоненту Components/Layout/NavMenu.razor
и добавим ещё одну ссылку в меню:
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()"> <nav class="flex-column"> <div class="nav-item px-3"> <NavLink class="nav-link" href="" Match="NavLinkMatch.All"> <span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home </NavLink> </div> //тут код для других ссылок <div class="nav-item px-3"> <NavLink class="nav-link" href="wrong" Match="NavLinkMatch.All"> <span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Not Found </NavLink> </div> </nav> </div>
здесь для новой ссылки в атрибуте href
мы указали несуществующий путь в нашем приложении:
<NavLink class="nav-link" href="wrong" Match="NavLinkMatch.All">
Теперь запустим приложение
Если кликнуть по новой ссылке, то мы увидим пустую страницу всего с одной строкой:
Теперь, чтобы пользователю вернуться на предыдущую страницу, придется перезагружать приложение. Воспользуемся свойством NoFound
компонента Router
следующим образом:
<Router AppAssembly="@typeof(MauiProgram).Assembly" > <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> <LayoutView Layout="@typeof(Layout.MainLayout)"> <p>Страница не найдена....</p> <a href="/">Вернуться на главную</a> </LayoutView> </NotFound> </Router>
Здесь, внутри <NotFound>
мы воспользовались новым для нас компонентом — <LayoutView>
, который отображает произвольное содержимое внутри заданного макета приложения. В качестве макета мы указали единственный макет нашего приложения — MainLayout.razor
. Теперь снова запустим приложение и перейдем по ошибочной ссылке:
Как видно, теперь пользователь может спокойно вернуться на домашнюю страницу приложения, нажав на ссылку. При этом макет приложения не «ломается» и мы видим весь пользовательский интерфейс.
Шаблоны маршрутов
Компоненты Razor могут содержать несколько директив @page
. Например, изменим компонент Counter
следующим образом:
@page "/counter" @page "/counter/path" @page "/counter/path/any" <h1>Counter</h1> <p role="status">Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
Теперь компонент можно загрузить, используя три пути:
/counter
/counter/path
/counter/path/any
Параметры маршрута
Используя шаблоны маршрутов в Blazor, мы можем передавать какие-либо данные через его параметры. Параметр маршрута определяется в фигурных скобках {имя_параметра}
. По умолчанию, маршрутизатор использует параметры маршрута для заполнения соответствующих параметров компонента с таким же именем (без учёта регистра). Например, изменим компонент Counter следующим образом:
@page "/counter" @page "/counter/with_name/{title}" @if (string.IsNullOrEmpty(Title)) { <h1>Counter</h1> } else { <h1>@Title</h1> } <p role="status">Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { [Parameter] public string Title { get; set; } private int currentCount = 0; private void IncrementCount() { currentCount++; } }
Во второй директиве @page мы используем параметр {title}
, который будет использоваться для параметра компонента Title
. Теперь добавим в компонент NavMenu ещё одну ссылку:
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()"> <nav class="flex-column"> //тут другие ссылки <div class="nav-item px-3"> <NavLink class="nav-link" href="counter"> <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter </NavLink> </div> <div class="nav-item px-3"> <NavLink class="nav-link" href="counter/with_name/Счётчик"> <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter </NavLink> </div> //тут другие ссылки </nav> </div>
Запустим приложение и сначала перейдем по первой ссылке для счётчика. Компонент будет выглядеть также, как и ранее:
При переходе по второй ссылке значение параметра шаблона будет присвоено параметру компонента Title и мы увидим:
Ограничения маршрута
При необходимости мы можем указать ограничение для параметра маршрута, используя конструкцию:
{параметр:ограничение}
В Blazor могут применяться следующие ограничения:
Ограничение | Пример | Примеры совпадений |
---|---|---|
bool |
{active:bool} |
true , FALSE |
datetime |
{dob:datetime} |
2016-12-31 , 2016-12-31 7:32pm |
decimal |
{price:decimal} |
49.99 , -1,000.01 |
double |
{weight:double} |
1.234 , -1,001.01e8 |
float |
{weight:float} |
1.234 , -1,001.01e8 |
guid |
{id:guid} |
CD2C1638-1638-72D5-1638-DEADBEEF1638 , {CD2C1638-1638-72D5-1638-DEADBEEF1638} |
int |
{id:int} |
123456789 , -123456789 |
long |
{ticks:long} |
123456789 , -123456789 |
Например, перепишем компонент Counter
следующим образом:
@page "/counter" @page "/counter/{value:int}" @if (string.IsNullOrEmpty(Title)) { <h1>Counter</h1> } else { <h1>@Title</h1> } <p role="status">Current count: @Value</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { [Parameter] public string Title { get; set; } [Parameter] public int Value { get; set; } private void IncrementCount() { Value++; } }
Теперь компонент доступен с использованием двух путей, причем, во втором пути мы применяем ограничение для параметра {value:int}
. То есть мы можем передавать в параметре только целые числа. Теперь вернемся к компоненту NavMenu
и изменим ссылки для компонента:
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()"> <nav class="flex-column"> //тут другие ссылки <div class="nav-item px-3"> <NavLink class="nav-link" href="counter"> <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter </NavLink> </div> <div class="nav-item px-3"> <NavLink class="nav-link" href="counter/Счётчик"> <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter </NavLink> </div> <div class="nav-item px-3"> <NavLink class="nav-link" href="counter/10"> <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter (10) </NavLink> </div> //тут другие ссылки </nav> </div>
Обратите внимание на вторую ссылку для которой указан путь counter/Счётчик
. Здесь мы явно нарушаем ограничение параметра. Попробуем запустить приложение и пройти по всем трем ссылкам:
Для второй ссылки (с нарушением ограничения) маршрутизатор не найдет соответствие и мы получим ошибку:
Так как маршрутизатор ожидает во втором сегменте пути получить целое число, а мы передаем строку, то мы получаем срабатывание параметра NotFound
компонента Router
.
Третья ссылка сработает как и ожидается — счётчику будет назначено начальное значение:
Одной из важных ролей, которые играют ограничения маршрута является разрешение неоднозначности в шаблонах маршрута. Например, мы решим задать вот такие пути для компонента:
@page "/counter" @page "/counter/{value}" @page "/counter/{title}" @if (string.IsNullOrEmpty(Title)) { <h1>Counter</h1> } else { <h1>@Title</h1> } <p role="status">Current count: @Value</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { [Parameter] public string Title { get; set; } [Parameter] public int Value { get; set; } private void IncrementCount() { Value++; } }
На первый взгляд, в коде компонента нет каких-либо проблем — параметр маршрута title
будет сопоставлен с Title
, а value
— с параметром компонента Value
. Однако, как только вы запустите приложение, то сразу же получите ошибку:
Нажмите F12
, чтобы посмотреть текст ошибки в консоли разработчика:
Вот такую неоднозначность мы и можем решить с использованием ограничений. Добавим ограничение:
@page "/counter" @page "/counter/{value:int}" @page "/counter/{title}"
Теперь приложение запустится и, более того, корректно будут работать все три ссылки меню:
Использование нескольких параметров маршрута в одном сегменте
Чтобы использовать два параметра маршрута в одном сегменте пути они должны быть разделены каким-либо разделителем. Например, если мы попытаемся задать вот такой шаблон маршрута:
@page "/counter/{value:int}{title}"
То получим ошибку:
Поэтому, если мы хатим передать в одном сегменте несколько параметров, то необходимо задавать шаблон пути, например, так:
@page "/counter/{value:int}-{title}"
Необязательные параметры
Для того, чтобы задать необязательный параметр, используются две конструкции:
{параметр?} {параметр:ограничение?}
то есть, вне зависимости имеет параметр ограничение или нет, для необязательного параметра перед закрывающей скобкой ставится символ ?
. Само название таких параметров говорит само за себя — такие параметры необязательно указывать в пути.
Универсальные параметры маршрута
Универсальные параметры захватывают несколько сегментов в пути. Для задания такого параметра после открывающей скобки ставится символ *
, например
@page "/{*generic}" <h1>Hello, world!</h1> <p>Параметр generic: @Generic</p> @code{ [Parameter] public string? Generic { get; set; } }
Теперь изменим путь в ссылку для домашней страницы, например, так:
<div class="nav-item px-3"> <NavLink class="nav-link" href="/any/path/to/home/page" Match="NavLinkMatch.All"> <span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home </NavLink> </div>
Теперь запустим приложение и нажмем на ссылку «Home»:
Итого
За выполнение маршрутизации в приложении Blazor отвечает компонент Router
. При задании шаблонов маршрутов в компонентах Razor мы можем использовать параметры, значения которых передаются параметрам компонента. Параметры маршрута могут быть необязательными, универсальными, а также содержать различные ограничения.