Простое автозаполнение для Blazor
Одна из вещей, которые мне действительно нравятся в Blazor, — это то, как часто в вашем приложении легко реализовать функции, которые, если они понадобятся в серверном приложении, заставят вас протестировать свой JavaScript-фу или обратиться к стороннему компоненту. Одним из примеров является компонент автозаполнения, который извлекает оперативные данные из базы данных, соответствующие тем, которые пользователь вводит в элемент управления формы, и изменяет их по мере того, как пользователь вводит данные. В этой статье я покажу, как создать такой компонент для приложения Blazor WebAssembly и оформить его как раскрывающийся список.
В примере используется шаблон проекта приложения Blazor WebAssembly с выбранным параметром ASP.NET Core Hosted для создания трех проектов: клиентского, серверного и общего проекта. В примере я подключился к версии базы данных Northwind. Если вы не знаете, что это такое, найдите седовласого разработчика и спросите его. Когда я ввожу текст в элемент управления формы, база данных запрашивается, и клиенты, чьи имена содержат строку, которую я ввожу, возвращаются и отображаются в том, что выглядит как раскрывающийся список. Когда я выбираю один из предложенных вариантов, мой выбор подтверждается.
Сам элемент управления представляет собой обычный ввод текста, а результаты отображаются в ul
элементе. Они помещаются в div
элемент, для которого position
установлено значение relative
, что позволяет размещать дочерние элементы абсолютно внутри него. Это ключ к позиционированию неупорядоченного списка параметров таким образом, чтобы он создавал раскрывающийся список с элементом управления вводом. Вот CSS для компонента автозаполнения:
.autocomplete {
position: relative;
}
.autocomplete .options {
position: absolute;
top: 40px;
left: 0;
background: white;
width: 100%;
padding: 0;
z-index: 10;
border: 1px solid #ced4da;
border-radius: 0.5rem;
box-shadow: 0 30px 25px 8px rgba(0, 0, 0, 0.1);
}
.autocomplete .option {
display: block;
padding: 0.25rem;
}
.autocomplete .option .option-text {
padding: 0.25rem 0.5rem;
}
.autocomplete .option:hover {
background: #1E90FF;
color: #fff;
}
.autocomplete .option.disabled {
background-color: lightgrey;
cursor: not-allowed;
}
.autocomplete .option.disabled:hover {
background: lightgrey;
color: var(--bs-body);
}
Я добавил к параметрам закругленные углы и тень блока, чтобы они более точно имитировали внешний вид раскрывающегося списка в браузере. Синий фон, который применяется к параметрам при наведении курсора, соответствует фону, применяемому в браузере Chrome. Я также объявил disabled
стиль, который будет применяться к сообщению, отображаемому в случае отсутствия совпадений. Далее я перейду к разделу кода компонента. Это содержит его данные и поведение:
@code {
List<Customer>? customers;
string? selectedCustomerId;
string? selectedCustomerName;
string? filter;
async Task HandleInput(ChangeEventArgs e)
{
filter = e.Value?.ToString();
if (filter?.Length > 2)
{
customers = await http.GetFromJsonAsync<List<Customer>>($"/api/companyfilter?filter={filter}");
}
else
{
customers = null;
selectedCustomerName = selectedCustomerId = null;
}
}
void SelectCustomer(string id)
{
selectedCustomerId = id;
selectedCustomerName = customers!.First(c => c.CustomerId.Equals(selectedCustomerId)).CompanyName;
customers = null;
}
}
Объявлен ряд полей. Первый List<Customer>
представляет собой параметры, возвращаемые сервером. Второй представляет значение ключа выбранного клиента, а третий — имя выбранного клиента. Последнее поле будет использоваться для хранения значения, введенного в элемент управления вводом.
Добавляется обработчик события named HandleInput
, который будет привязан к input
событию элемента управления вводом. Он срабатывает для каждого символа, который вводится или удаляется из элемента управления вводом. Обработчик проверяет длину входного значения и, если оно составляет три символа или более, вызывает API, который возвращает список клиентов, назначенных customers
полю. Если входное значение меньше трех символов, customers
поле устанавливается в null
, вместе с selectedCustomerName
и selectedCustomerId
.
Другой метод SelectCustomer
отвечает за назначение selectedCustomerId
и на selectedCustomerName
основе строкового параметра, представляющего значение ключа выбранного клиента. Он также отвечает за установку в customer
поле значения null
, что очищает все параметры.
API — это минимальная конечная точка API, зарегистрированная в файле Program.cs серверного проекта :
app.MapGet("/api/companyfilter", async (string filter, [FromServices] ICustomerManager manager) =>
Results.Ok(await manager.GetFilteredCustomerNames(filter))
);
Он вызывает метод реализации ICustomerManager
интерфейса, показанного ниже:
public class CustomerManager : ICustomerManager
{
private readonly NorthwindContext context;
public CustomerManager(NorthwindContext context) => this.context = context;
public async Task<List<Customer>> GetFilteredCustomerNames(string filter) =>
await context.Customers
.Where(c => c.CompanyName.ToLower().Contains(filter.ToLower()))
.OrderBy(c => c)
.ToListAsync();
}
Этот метод получает все компании, названия которых содержат введенный текст. Названия компаний и значение фильтра в моем примере преобразуются в нижний регистр, потому что я использую базу данных SQLite, где по умолчанию сравнение строк учитывает регистр.
Вернемся к самому компоненту автозаполнения, вот код для части пользовательского интерфейса:
@page "/autocomplete"
@inject HttpClient http
<h3>Autocomplete Demo</h3>
<div class="autocomplete w-25">
<input @bind=selectedCustomerName @oninput=HandleInput class="form-control filter" />
@if (customers is not null)
{
<ul class="options">
@if (customers.Any())
{
@foreach (var customer in customers)
{
<li class="option" @onclick=@(_ => SelectCustomer(customer.CustomerId))>
<span class="option-text">@customer.CompanyName</span>
</li>
}
}
else
{
<li class="disabled option">No results</li>
}
</ul>
}
</div>
@if (!string.IsNullOrWhiteSpace(selectedCustomerName))
{
<p class="mt-3">
Selected customer is @selectedCustomerName with ID <strong>@selectedCustomerId</strong>
</p>
}
Служба HttpClient
внедряется в компонент, чтобы HandleInput
метод мог использовать ее для получения данных по мере ввода пользователем. Два блока представляют наибольший интерес. Второй из них появляется только в том случае, если было выбрано имя клиента. Он содержит контент, подтверждающий детали выбора. Первый блок — это div
класс с autocomplete
примененным к нему классом. Он содержит ввод с selectedCustomerName
привязкой к его значению и HandleInput
методу, привязанному к его @oninput
атрибуту. Если какие-либо совпадающие записи клиентов возвращаются из HandleInput
метода, ul.otpions
отображается, и отдельные параметры отображаются для элементов списка, к которым SelectCustomer
привязан метод .onclick
обработчик. Каждый из них принимает идентификатор текущего клиента в качестве параметра. Помните, что это метод, который устанавливает selectedCustomerName
и удаляет все параметры. Если совпадающие результаты не найдены, пользователь получает соответствующее уведомление:
Исходный код этой статьи доступен по адресу https://github.com/mikebrind/Blazor-Autocomplete .
Резюме
И вот оно. Никаких сторонних библиотек, никакого JavaScript. Просто очень простое автозаполнение, написанное с использованием C# и Razor, которое использует возможности платформы Blazor для отображения и скрытия элементов в зависимости от состояния данных компонента. Это почти похоже на обман.
Только полноправные пользователи могут оставлять комментарии. Аутентифицируйтесь пожалуйста, используя сервисы.