Маршрутизация в приложениях Blazor Hybrid

Маршрутизация в 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++;
    }
}

Теперь компонент можно загрузить, используя три пути:

  1. /counter
  2. /counter/path
  3. /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} trueFALSE
datetime {dob:datetime} 2016-12-312016-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}"

То получим ошибку:

A path segment cannot contain two consecutive parameters. They must be separated by a ‘/’ or by a literal string.

Поэтому, если мы хатим передать в одном сегменте несколько параметров, то необходимо задавать шаблон пути, например, так:

@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 мы можем использовать параметры, значения которых передаются параметрам компонента. Параметры маршрута могут быть необязательными, универсальными, а также содержать различные ограничения.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии