跳到正文

控件和选择器

本页译自英文。 阅读英文页面

表单字段根据其类型选择控件。.control(M::f, Control::...) 更改控件,.lookup(M::f, action) 将字段变成对服务器的搜索。每个控件底层都是原生表单元素,因此无论有没有运行时,值的提交方式都相同,并且每个控件都遵循其 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复选框
#[choice] 枚举下拉选择:点击后打开可搜索列表的按钮
#[choice] 枚举的 Vec多选:以标签(chip)显示,并用列表选择多个选项
Date、Time、DateTime、LocalDateTime日期和时间选择器
模型的 Vec<M>行编辑器(参见行编辑器)

其他控件

控件用途
Control::Radio选项较少且全部可见的选择
Control::Segmented有两到五个简短选项的选择
Control::Listbox带输入搜索的长选项列表
Control::Switch作为设置立即生效的 bool
Control::Percent带百分号显示的 Decimal 或整数
Control::Password、Control::NewPassword按密码类型输入的文本字段,带显示和隐藏按钮
Control::PasswordMeter带强度指示条的新密码
Control::Rating1 到 N 的整数,以星星显示(见 Rating)

Rating

Control::Rating 把整数字段显示为 1 到 N 颗星,N 为字段的 max 规则(没有规则时为 5,最大为 10)。它是放在 fieldset 中的一组原生单选按钮,fieldset 的 legend 就是字段标签,因此无需脚本即可遵循 W3C APG 单选组模式:Tab 只进入该组一次,方向键移动并选中,每颗星以用户的语言命名为“3 / 5”。鼠标悬停时,星星会亮到指针所在位置。服务器像校验其他数字一样用字段规则校验该值,所以请为字段设置 min 和 max;在 Option<i64> 上使用 required 时,不选任何星星会被拒绝。

#[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 在用户输入时于服务器上搜索记录。action 接收 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")]) 在列表中显示多列。搜索仍然发送到服务器上的 action,每次按键的搜索都会检查其权限;每个条目通过 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()))
}

多选

#[choice] 枚举的 Vec 使用多选进行编辑:所选选项显示为带移除按钮的标签(chip),组合框打开一个列表,在其中开启或关闭选项(APG listbox,带 aria-multiselectable,位于组合框内)。底层是原生的 <select multiple>,因此字段在没有运行时的情况下也能工作。运行时以逗号连接的形式提交值(Email,Sms);服务器逐个读取,拒绝未知值并去除重复项。在 JSON(API、AI 工具、数据库)中,该字段是选项值的数组。

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

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

按键作用
Arrow Down、Arrow Up、Enter、Space在已关闭的组合框上打开列表
Home、End打开列表并定位到第一个或最后一个选项
Arrow Down、Arrow Up在已打开的列表中移动
Shift 加 Arrow Down 或 Arrow Up移动并开启或关闭下一个选项
Space、Enter开启或关闭当前选项;列表保持打开
字母跳到下一个以这些字母开头的选项
Backspace、Delete在已关闭的组合框上:移除最后选择的选项
Escape、Alt 加 Arrow Up关闭列表;焦点保持在组合框上
Tab关闭列表并继续移动

Ctrl+A(APG 模式中的全选)按照快捷键策略的要求交给浏览器处理;标签的移除按钮在 Tab 顺序中。列表、标签和 lookup 各列共需约 1.7 KB gzip 的脚本,没有请求;该字段是 3 个节点加上每个所选选项 1 个。

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
其他任何字符字面量:显示,但从不存储

没有运行时的情况下,该字段是保存原始值的普通文本输入框;用户输入时可以带也可以不带字面量。

密码

Control::Password(登录)、Control::NewPassword(注册、修改)和 Control::PasswordMeter(带强度指示条的新密码)是密码输入框,绝不会由服务器填充,也绝不会保存在草稿中。显示和隐藏按钮遵循 APG 切换按钮模式:其名称始终是“Show password”,aria-pressed 表明文本是否显示。表单提交时会将密码重新切换为隐藏。

指示条是浏览器根据长度、字符种类和明显模式计算出的提示,以四个条和一段文字(“Strength: good”)显示在礼貌型(polite)live region 中。服务器接受什么由字段的规则(min_len、模型的 validate 函数)决定,绝不由指示条决定。

百分比

键盘

按键位置作用
Arrow Up、Arrow Downdecimal 和百分比输入框在 min 和 max 范围内按一个整数单位步进
Page Up、Page Downdecimal 和百分比输入框按十步进
Backspace掩码输入框删除光标前的字符;在字面量之上时,删除该字面量之前的字符
Tab密码输入框移到显示和隐藏按钮
Enter、Space显示和隐藏按钮显示或隐藏密码;焦点保持在按钮上

开销与检查

新增脚本components.js 中约 2 KB gzip(7 KB 原始),用于掩码、切换和指示条;decimal 和百分比输入框使用 number 控件
DOM 节点掩码输入框 3 个,带切换按钮的密码 4 个,带指示条 11 个
请求输入时没有
服务器检查scale、precision、min、max 和 mask 在每次保存时运行,页面、API 和 AI 工具一视同仁

日期和时间选择器

选择器接受用户格式的键入输入,并打开日历或时间列表。DateOptions 设置限制:.no_weekends()、.week_numbers()、.step_minutes(15)。.range(from, to) 将两个日期字段合并为一个范围选择器,带有诸如 This month 的预设。在线示例见时间和语言。