本文介绍如何通过 Actions 设置字段的 HTML attributes。
attributes 通过 setAttribute 写在字段组件的宿主元素上。下方示例:悬停输入框看 title;敲击键盘,日志会实时读出宿主上的 data-role / data-length(patchAsync 随值更新)。
api-attributes.definition.ts
import * as v from 'valibot';
import { map } from 'rxjs';
import { actions, NFCSchema, setComponent } from '@piying/view-angular-core';
export const schema = v.pipe(
v.object({
input: v.pipe(
v.string(),
actions.attributes.set({
placeholder: '悬停输入框看 title,敲击看 data-length',
title: '0',
}),
// patchAsync:随值变化动态更新 data-length(可在开发者工具中观察)
actions.attributes.patchAsync({
title: (field) =>
field.form.control!.valueChanges.pipe(
map((value) => String((value as string)?.length ?? 0)),
),
}),
),
}),
setComponent('fieldset'),
);
将指定对象设置为字段的 HTML attributes:
import { actions } from '@piying/view-angular-core';
const schema = v.pipe(
v.string(),
actions.attributes.set({
'data-testid': 'username-field',
'aria-label': '请输入用户名',
autocomplete: 'username',
}),
);
在已有属性上合并新的键值对:
const schema = v.pipe(
v.string(),
actions.attributes.set({ 'data-testid': 'name' }),
actions.attributes.patch({ readonly: true }),
);
// 最终 attributes = { 'data-testid': 'name', readonly: true }
const schema = v.pipe(
v.string(),
actions.attributes.set({ 'data-testid': 'name', autocomplete: 'on' }),
actions.attributes.remove(['autocomplete']),
);
// 最终 attributes = { 'data-testid': 'name' }
通过字段引用动态创建异步属性值:
const schema = v.pipe(
v.string(),
actions.attributes.patchAsync({
'data-value': (field) => field.form.control?.value ?? '',
}),
);
与 patchAsync「按 key 赋值」不同,mapAsync 拿到整个 attributes 对象做函数式变换。写法是 (field) => (value) => 新值:
const schema = v.pipe(
v.string(),
actions.attributes.set({ 'data-role': 'input', 'data-len': '0' }),
// 给所有 key 加上命名空间前缀,并注入当前值的长度
actions.attributes.mapAsync((field) => {
return (value: Record<string, any>) => {
const len = String(field.form.control?.value?.length ?? 0);
return Object.fromEntries(
Object.entries(value).map(([k, v]) => [`piying-${k}`, k === 'data-len' ? len : v]),
);
};
}),
);
// 最终 attributes = { 'piying-data-role': 'input', 'piying-data-len': '<当前输入长度>' }
mapAsync 适合「批量改写已有属性」的场景(加前缀、脱敏、统一注入)。只是根据字段状态设几个值时,用 patchAsync 更直接。
top.set / top.patch 把属性写到字段的最外层 wrapper(无 wrapper 时为字段自身),常用于由 wrapper 渲染的卡片/fieldset 等容器:
const schema = v.pipe(
v.string(),
actions.wrappers.set(['card']),
actions.attributes.top.set({ id: 'my-card' }), // id 设在 card wrapper 上
actions.attributes.top.patch({ 'data-role': 'field-card' }),
);
| 特性 |
inputs |
attributes |
| 作用目标 |
组件的 @Input() 属性 |
HTML 原生 attribute |
| 用法 |
actions.inputs.set({ placeholder: '...' }) |
actions.attributes.set({ 'data-testid': '...' }) |
| 绑定方式 |
Angular 属性绑定 [inputName] |
HTML 属性直接设置 |
| 适用场景 |
组件间数据传递 |
DOM 属性、ARIA 标签、自定义 data-* |
import * as v from 'valibot';
import { actions, setComponent } from '@piying/view-angular-core';
const schema = v.object({
username: v.pipe(
v.string(),
setComponent('input'),
// 组件输入属性
actions.inputs.set({
label: '用户名',
placeholder: '请输入 2-20 个字符',
}),
// HTML attributes
actions.attributes.set({
'data-testid': 'username-input',
'aria-required': 'true',
autocomplete: 'username',
}),
),
email: v.pipe(
v.string(),
setComponent('input'),
actions.inputs.set({ type: 'email' }),
actions.attributes.set({
'aria-label': '邮箱地址',
}),
),
});