Перейти к содержимому

Элементы управления и выбор значений

Эта страница переведена с английского. Читать страницу на английском

Поля формы выбирают элемент управления по своему типу. .control(M::f, Control::...) меняет его, а .lookup(M::f, action) превращает поле в поиск на сервере. Каждый элемент управления внутри это нативный элемент формы, поэтому значения отправляются одинаково с runtime и без него, а каждый элемент следует своему шаблону ARIA для пользователей клавиатуры и программ чтения с экрана.

Form::of(Contact { quantity: 2, size: Size::Medium, ..Contact::default() })
    .lookup(Contact::product, find_samples)
    .row(|r| r.field(Contact::delivery).field(Contact::size))
    .control(Contact::delivery, Control::Radio)
    .control(Contact::size, Control::Segmented)
    .field(Contact::newsletter)
    .control(Contact::newsletter, Control::Switch)
    .field(Contact::note)
    .submit(t!("docs.live.send"), send_contact)

Delivery

Size

0 из 300 символов

Элементы по умолчанию

Тип поляЭлемент управления
StringТекстовое поле; растущая текстовая область со счётчиком символов, когда max_len больше 255
i32, i64Числовое поле с правилами min и max
MoneyСумма в числовом формате пользователя, с валютой
DecimalТочное число в формате пользователя, с scale поля (см. Ввод Decimal)
boolФлажок
enum с #[choice]Select: кнопка, открывающая список с поиском
Vec из enum с #[choice]Множественный выбор: чипы и список, где выбирают несколько вариантов
Date, Time, DateTime, LocalDateTimeВыбор даты и времени
Vec<M> моделиРедактор строк (см. Редактор строк)

Другие элементы управления

Элемент управленияДля чего
Control::RadioВыбор из нескольких вариантов, все видны
Control::SegmentedВыбор из двух-пяти коротких вариантов
Control::ListboxДлинный список выбора с поиском по вводу
Control::Switchbool, который действует как настройка
Control::PercentDecimal или целое число со знаком процента
Control::Password, Control::NewPasswordТекстовое поле типа пароль, с кнопкой показать и скрыть
Control::PasswordMeterНовый пароль с индикатором надёжности
Control::RatingЦелое число от 1 до N, показанное звёздами (см. Rating)

Rating

Control::Rating показывает целочисленное поле в виде звёзд от 1 до N, где N задаётся правилом max поля (5 без правила, не более 10). Это группа нативных радиокнопок внутри fieldset, чья legend является подписью поля, поэтому без скрипта она следует паттерну W3C APG radio group: Tab попадает в группу один раз, стрелки перемещают и отмечают, а каждая звезда называется «3 из 5» на языке пользователя. При наведении подсвечиваются звёзды до указателя. Сервер проверяет значение правилами поля, как любое число, поэтому задайте полю min и max; при required для Option<i64> пустой выбор отклоняется.

#[model]
pub struct Feedback {
    #[rule(required, min = 1, max = 5)]
    pub stars: Option<i64>,
}

Form::of(Feedback::default())
    .field(Feedback::stars)
    .control(Feedback::stars, Control::Rating)
    .submit(t!("feedback.send"), send_feedback)
Stars

Lookup

Lookup ищет записи на сервере по мере ввода. Действие принимает LookupQuery и возвращает Page<LookupItem>; поле хранит id.

#[action(permission = "stock.product.read")]
pub async fn find_samples(q: LookupQuery, store: SampleStore) -> Result<Page<LookupItem>> {
    let items = store.all()?.into_iter().map(|s| LookupItem::new(s.id.0, s.name).hint(s.code)).collect();
    Ok(q.apply(items))
}

.lookup_many(M::f, action) делает то же для поля Vec<i64> и показывает выбранные значения как теги, которые можно удалить.

.lookup_columns(M::f, action, [t!("code"), t!("name"), t!("city")]) показывает в списке несколько колонок. Поиск по-прежнему идёт в действие на сервере, а его разрешение проверяется при каждом поиске по нажатию клавиши; каждый элемент отдаёт свои значения через LookupItem::cells в порядке заголовков. LookupQuery::apply ищет и по ячейкам. Программы чтения с экрана произносят каждый вариант как "Code: C-2, Name: Mert Kaya, City: Ankara". На узких экранах колонки складываются внутри каждого варианта.

#[action(permission = "sales.customer.read")]
pub async fn find_customers(q: LookupQuery, store: CustomerStore) -> Result<Page<LookupItem>> {
    let items = store.all()?.into_iter().map(|c| LookupItem::new(c.id.0, c.name.clone()).cells([c.code, c.name, c.city]));
    Ok(q.apply(items.collect()))
}

Множественный выбор

Vec из enum с #[choice] редактируется множественным выбором: выбранные варианты показываются как чипы с кнопкой удаления, а combobox открывает список, где варианты включаются и выключаются (APG listbox с aria-multiselectable внутри combobox). Внутри лежит нативный <select multiple>, поэтому поле работает без runtime. Runtime отправляет значения через запятую (Email,Sms); сервер читает их по одному, отклоняет неизвестные значения и убирает повторы. В JSON (API, инструменты AI, база данных) поле это массив значений вариантов.

#[model]
pub struct Contact {
    #[rule(required)]
    pub channels: Vec<Channel>,
}

Form::of(contact).field(Contact::channels)

КлавишаДействие
Стрелка вниз, стрелка вверх, Enter, пробелОткрывают список на закрытом combobox
Home, EndОткрывают список на первом или последнем варианте
Стрелка вниз, стрелка вверхПеремещают по открытому списку
Shift со стрелкой вниз или вверхПеремещает и включает или выключает следующий вариант
Пробел, EnterВключают или выключают активный вариант; список остаётся открытым
БуквыПереходят к следующему варианту, который с них начинается
Backspace, DeleteНа закрытом combobox: удаляют последний выбранный вариант
Escape, Alt со стрелкой вверхЗакрывают список; фокус остаётся на combobox
TabЗакрывает список и переводит фокус дальше

Ctrl+A (выбрать всё в шаблоне APG) оставлен браузеру, как требует политика сочетаний клавиш; кнопки удаления на чипах входят в порядок обхода по Tab. Список, чипы и колонки lookup требуют около 1,7 КБ gzip скрипта и ни одного запроса; поле это 3 узла плюс по одному на каждый выбранный вариант.

Decimal и проценты

Поля Decimal хранят точные числа: нигде между вводом, сервером, базой данных и экспортом нет чисел с плавающей запятой. Поле ввода форматирует число на языке пользователя по мере набора (1.234,5678 по-турецки, 1,234.5678 по-английски) и допускает столько знаков после запятой, сколько задаёт scale поля; сервер читает его на том же языке и заново проверяет scale, precision, min и max.

#[model]
pub struct Terms {
    #[rule(precision = 12, scale = 4, min = 0)]
    pub rate: Decimal,
    #[rule(scale = 1, min = 0, max = 100)]
    pub discount: Option<Decimal>,
}

Form::of(terms).field(Terms::rate).field(Terms::discount).control(Terms::discount, Control::Percent)

Control::Percent хранит значение в процентных пунктах: 12.5 показывается как 12.5 % по-английски и %12,5 по-турецки, а сохраняется как 12.5. Программы чтения с экрана произносят слово "percent" через описание поля.

Ввод по маске

Текстовое поле с правилом mask показывает свой шаблон во время ввода и отправляет только исходные символы. Шаблон и есть правило: сервер убирает набранные литералы ((532) 123 45 67 становится 5321234567), приводит слоты A к верхнему регистру и отклоняет значение, которое заполняет не каждый слот. Вызовы API и инструменты AI отправляют исходное значение и получают ту же проверку.

#[model]
pub struct Contact {
    #[rule(required, mask = "(999) 999 99 99")]
    pub phone: String,
    #[rule(mask = "AA99 9999 9999 9999 9999 9999 99")]
    pub iban: Option<String>,
}
Символ шаблонаЗначение
9Цифра
aБуква
AБуква, записанная в верхнем регистре
*Буква или цифра
обратная косая чертаСледующий символ это литерал, например \T
любой другойЛитерал: показывается, но не сохраняется

Без runtime поле это обычное текстовое поле с исходным значением; пользователи могут вводить его с литералами или без них.

Пароль

Control::Password (вход), Control::NewPassword (регистрация, смена) и Control::PasswordMeter (новый пароль с индикатором надёжности) это поля пароля, которые никогда не заполняются с сервера и никогда не попадают в черновики. Кнопка показать и скрыть следует шаблону APG toggle button: её имя остаётся "Show password", а aria-pressed сообщает, виден ли текст. При отправке форма снова скрывает пароль.

Индикатор это подсказка, которую браузер вычисляет по длине, видам символов и очевидным шаблонам; он показывается четырьмя полосками и текстом ("Strength: good") в вежливой живой области (polite live region). Что принимает сервер, решают правила поля (min_len, функция validate модели), но никогда не индикатор.

процентов

Клавиатура

КлавишаГдеДействие
Стрелка вверх, стрелка внизполя decimal и процентовШаг в одну целую единицу в пределах min и max
Page Up, Page Downполя decimal и процентовШаг в десять
Backspaceполе с маскойУдаляет символ перед кареткой; над литералом удаляет символ перед литералом
Tabполе пароляПереходит к кнопке показать и скрыть
Enter, пробелкнопка показать и скрытьПоказывает или скрывает пароль; фокус остаётся на кнопке

Бюджет и проверки

Добавленный скриптоколо 2 КБ gzip (7 КБ исходного) в components.js для масок, переключателя и индикатора; поля decimal и процентов используют числовой элемент
Узлы DOM3 для поля с маской, 4 для пароля с переключателем, 11 с индикатором
Запросынет во время ввода
Проверки на сервереscale, precision, min, max и mask выполняются при каждом сохранении, одинаково со страницы, из API и из инструментов AI

Выбор даты и времени

Элементы выбора принимают ввод в формате пользователя и открывают календарь или список времени. DateOptions задаёт ограничения: .no_weekends(), .week_numbers(), .step_minutes(15). .range(from, to) объединяет два поля даты в один выбор диапазона с готовыми вариантами, например This month. Живой пример находится на странице Время и языки.