Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[fields/components] 一个 multiple: true 的 select 字段在对象表单里 host label 仍然惰性 —— 按字段类型键入的 labelling 声明碰不到它(#3975 修完后的实测残留) #3986

Closed

Description

@yinlianghui

在 #3975(PR #3983,给 multiselect 加 labelling: 'group')的验证过程中顺手实测到的一条,不夹带进那个 PR:同一失效类的另一条路径,而且它无法用「往 FIELD_TYPES_GROUP_LABELLED 里再加一项」来修。

实测(在 #3975 的修复已生效的树上)

一次性 probe(未提交),真表单 + registerAllFields() 真惰性注册路径:

type: 'multiselect'                    → byRoleGroupNamed=1  role=group   ← #3975 已修
type: 'field:select', multiple: true   → for=…-form-item 解析到 div
                                         byRoleGroupNamed=0  byLabelText=0  ← 本单

为什么 #3975 的修法碰不到它

三段都是现成的、各自都正确的代码,拼起来漏掉一格:

  1. packages/fields/src/field-type-alias.ts:45 —— mapFieldTypeToFormType 只按字段的 type 字符串映射:select: 'field:select',不看 multiple;
  2. packages/plugin-form/src/sectionFields.ts:101,109 —— 对象表单据此发出 type: 'field:select',并显式把 multiple: field.multiple 抄进 form field(ObjectForm.tsx:567 同样走 mapFieldTypeToFormType,并以 field: field 携带整份元数据);
  3. packages/fields/src/widgets/SelectField.tsx:36-41 —— SelectField 见到 config.multiple 就委派给 MultiSelectField,渲染出 chip 行那个包裹 div。

于是渲染的是 MultiSelectField,但决定 label 怎么关联的声明是按 select 这个键查的(resolveFieldLabelling → getMeta('select')),而 select 必须保持未声明 —— 单选的 trigger 是可 label 的,给它剥掉 for 是把一个能用的字段弄坏(form-group-label-association.test.ts 里已有一条钉专门守这个方向:「a BUILTIN type ignores a registry declaration under the same name」)。

所以 host label 照旧发 for,指向 chip 行的 div,而 label[for] 指向不可 label 的元素是惰性的(HTMLLabelElement.control 返回 null):视觉上有标签,屏幕阅读器听不到这一组是什么。与 #3975 / #3961 同一个事实,只是入口不同。

可达性:这不是只能手写 SDUI 才碰到的死角

spec 里 multiple 对 select 合法,而上面第 2 步是对象表单的正常路径。也就是说一个 { type: 'select', multiple: true } 的普通 picklist 字段,在真实对象表单里就是这个形态。(metadata-admin 自己的 SchemaForm.tsx:197 走的是另一个映射器,它 multiple ? 'multiselect' : 'select' —— 恰好绕过了本问题,这也是它一直没被发现的原因。)

处置需要裁决(所以不自行实施)

  • A:让生产端决定 widget —— mapFieldTypeToFormType 对 select + multiple 发 field:multiselect。声明保持「按类型的纯事实」,SelectField 的委派分支在这条路径上随之变成死码。契约优先、结构上不易错,但有波及面:CASCADE_OPTION_FIELD_TYPES / DATA_SOURCE_FIELD_TYPES 的匹配、以及任何按 field:select 判断的地方都要一起看,而且它需要一个值(不只是类型)才能算出 widget 名 —— 得确认所有调用点都拿得到 multiple。
  • B:让 resolveFieldLabelling 读字段配置的 multiple —— 改动最小,但把「labelling 是每个类型的一个事实」变成值相关的判断,声明位从此不再是纯声明;[fields] 复合/分组 field widget 丢弃或错投 host 下发的控件 id:address / geolocation 的表单 label for 悬空,checkboxes / radio / rating / file 的 for 落在不可 label 的 div 上 #3961 刻意把它做成声明而不是让 renderer 猜 DOM,B 是往回走一步。
  • C:什么都不做,只在文档/测试里记下 —— 只有在认定该形态实际不可达时才成立,而上面第 2 段说明它可达。

倾向 A(长期正确:一处决定「渲染哪个 widget」,声明与渲染因此不可能再错位),但它的波及面值得先量一遍再动手 —— 交给三分诊定。

参考

Activity

  1. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    Contributor

    Triage: pm:queue (objectui-local card — sibling repos carry pm-state only, no domain:*).

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    ContributorAuthor

    PM 分诊轮(session_01GTRjn8xBqp75dk7kFupVRt):方向代裁 A 案 —— mapFieldTypeToFormType 对 select + multiple: true 发 field:multiselect,生产端一处决定渲染哪个 widget,声明与渲染从此不可能错位。理由:与 #3961 把 labelling 做成「按类型的纯声明」的哲学一致;B 案把声明位变值相关是明确的倒退(正文自己论证了);C 案因可达性不成立。实施前置:先量波及面(正文点名的 CASCADE_OPTION_FIELD_TYPES / DATA_SOURCE_FIELD_TYPES / 一切按 field:select 判断的调用点是否拿得到 multiple),测量表进 PR;若测量发现某调用点结构上拿不到值,停手报告而非硬改 —— 届时升级为契约面裁决。SelectField 的委派分支在该路径成为死码后按 ADR-0049 语义处置(标注或收敛,量完定)。

    排程:与在飞 #3990 的 widgets 面相交(SelectField/MultiSelectField)—— #3990 收单后派,候选池就位。


    Generated by Claude Code

  3. self-assigned this
    on Aug 9, 2026
  4. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    ContributorAuthor

    【PM 认领】objectui 分片(session_01GTRjn8xBqp75dk7kFupVRt),批次 7 补位。派 os-dev 实施,分支 claude/issue-3986-select-multiple-widget-mapping(objectui 仓)。前置已满足:#3990/PR #4002 已落 main(c97a45e8c)。

    按已裁 A 案实施(测量前置,见分诊评论):mapFieldTypeToFormType 对 select + multiple: true 发 field:multiselect;波及面测量表先行进 PR;某调用点拿不到 multiple 则停手报告。面切割注记:MultiSelectField/各 widget 的只读分支属 #4005,本单不碰只读面。


    Generated by Claude Code

  5. added a commit that references this issue on Aug 10, 2026
    11c1e71
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions