
使用 DualListInput 编辑数组与多对多关系react-admin 双列穿梭选择器完整指南【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-adminDualListInput是 react-admin 企业版Enterprise Edition来自react-admin/ra-relationships包提供的双列穿梭选择组件用户通过在可选列表与已选列表之间移动条目即可编辑标量数组值如[u001, u003]、一对多或多对多关系。读完本文你将掌握其全部 props 的用法、choices数据的各种构造方式、如何嵌套在ReferenceArrayInput中编辑外键数组以及如何用sx与全局主题覆盖进行样式定制。DualListInput 是什么双列穿梭选择的定位DualListInput通过并排的两个列表让用户完成选择左侧列出所有可选条目右侧列出当前已选条目中间的按钮负责在两个列表之间移动条目。它没有下拉菜单、没有输入框联想交互直观尤其适合可选数量有限、但希望一眼看到全部选项及当前选中状态的场景。组件编辑的是数组值包括标量数组如[123, 456]一对多关系一个资源记录引用另一资源的多个 id多对多关系通过中间表关联的两组记录。该组件属于 react-admin 企业版组件需要安装独立的商业许可包react-admin/ra-relationships后按如下方式引入import { DualListInput } from react-admin/ra-relationships;从 文档索引 可以看到DualListInput在官方文档导航中带有企业版专属标记premium 徽标这也解释了为何它不出现在开箱即用的react-admin主包中而需要单独授权。与其他数组编辑组件的取舍react-admin 提供了多套编辑数组值的输入组件Inputs.md 数据类型表 将 Array of Enums如[foo, bar]这一数据类型明确列出了五个可选组件组件交互形态适用场景TextArrayInput芯片 自由输入数组值不可穷举如邮箱列表见 TextArrayInput 文档AutocompleteArrayInput自动补全下拉可选值很多需要输入搜索SelectArrayInput下拉多选可选值有限且需要节省表单空间CheckboxGroupInput复选框组可选值很少希望所有选项平铺展示DualListInput双列穿梭需要同时看到全部可选值与已选值当你编辑的数组是另一资源的 id 集合即外键数组时还应配合ReferenceArrayInput使用详见后文 在 ReferenceArrayInput 中使用。总的原则是DualListInput适合可选条目数量适中、用户需要对比和确认的场景如果参考资源条目非常多它因为没有自动补全能力而不太合适原文档明确提示了这一点。快速上手最简用法DualListInput在使用上比普通输入组件多一个必需概念除了source之外还需要一个choices属性来声明所有可能的取值。import { Create, SimpleForm } from react-admin; import { DualListInput } from react-admin/ra-relationships; const UserCreate () ( Create SimpleForm DualListInput sourceroles choices{[ { id: admin, name: Admin }, { id: u001, name: Editor }, { id: u002, name: Moderator }, { id: u003, name: Reviewer }, ]} / /SimpleForm /Create );默认情况下可选项按如下规则由choices构建id字段作为选项的值写入表单name字段作为选项显示的文本。source对应的表单值必须是已选值的数组例如{ id: 123, name: John Doe, roles: [u001, u003], }Props 总览原文档给出了完整属性表整理如下所有类型与默认值均以原文档为准PropRequiredTypeDefaultDescriptionchoicesOptionalObject[]-要作为选项展示的条目列表。除非位于ReferenceArrayInput内部否则必需。addButtonOptionaloutlined | contained | text |element-添加按钮的 Material UIvariant值或一个用于完全替换它的 React 元素addButtonLabelOptionalstringra-relationships.duallistinput.select添加按钮的文本或翻译 keyavailableItemsLabelOptionalstringra-relationships.duallistinput.availableItems可选列表标题的文本或翻译 keydenseOptionalbooleanfalse列表组件的视觉密度disableValueOptionalstringdisabled用于在choices中标记禁用选项的自定义字段名optionTextOptionalstring|Functionname用于显示选项文本的记录字段名或接收当前记录并返回字符串的函数record {string}optionValueOptionalstringid记录中用于作为输入值的字段名removeButtonOptionaloutlined | contained | text |element-移除按钮的 Material UIvariant值或一个用于完全替换它的 React 元素removeButtonLabelOptionalstringra-relationships.duallistinput.unselect移除按钮的文本或翻译 keyselectedItemsLabelOptionalstringra-relationships.duallistinput.selectedItems已选列表标题的文本或翻译 keytranslateChoiceOptionalbooleantrue选项文本是否应被翻译此外DualListInput还接受所有通用输入属性common input props包括source、label、defaultValue、disabled、readOnly、fullWidth、helperText、format、parse、validate、className与sx。按钮与列表标题的本地化由于组件属于企业版包其内置文案通过翻译 key 提供默认值分别是添加按钮ra-relationships.duallistinput.selectselect 语义即加入已选列表移除按钮ra-relationships.duallistinput.unselect可选列表标题ra-relationships.duallistinput.availableItems已选列表标题ra-relationships.duallistinput.selectedItems。如果你使用 polyglot 翻译体系可在应用的 messages 中覆盖这些 key也可以直接传字符串覆盖DualListInput sourceroles choices{choices} addButtonLabel加入 removeButtonLabel移除 availableItemsLabel可用角色 selectedItemsLabel已分配角色 /按钮外观默认使用 Material UI 的某种 variant可通过addButton/removeButton传入outlined、contained、text之一或传入自定义 React 元素完全替换。choices构造可选列表choices必须是对象数组——每个对象对应一个可选条目其中id是值name是展示给用户的标签DualListInput sourceroles choices{[ { id: admin, name: Admin }, { id: u001, name: Editor }, { id: u002, name: Moderator }, { id: u003, name: Reviewer }, ]} /用 disabled 字段禁用部分选项在某条选项上设置disabled字段即可将其渲染为禁用状态用户无法移动它DualListInput sourceroles choices{[ { _id: admin, label: Admin, disabled: true }, { _id: u001, label: Editor }, { _id: u002, label: Moderator }, { _id: u003, label: Reviewer }, ]} /注意禁用字段名默认是disabled想改用其他字段如not_available需配合disableValue属性详见后文。自定义标签字段与值字段如果选项对象的值字段、标签字段不叫id/name可以通过optionText与optionValue指定DualListInput sourceroles choices{[ { _id: admin, label: Admin }, { _id: u001, label: Editor }, { _id: u002, label: Moderator }, { _id: u003, label: Reviewer }, ]} optionValue_id optionTextlabel /使用翻译标识符作为选项文本choices默认会经过翻译函数处理因此可以直接把翻译 key 当作nameconst choices [ { id: admin, name: myroot.roles.admin }, { id: u001, name: myroot.roles.u001 }, { id: u002, name: myroot.roles.u002 }, { id: u003, name: myroot.roles.u003 }, ];这样当用户切换到其他语言时选项文本会自动跟随翻译。如果不想翻译选项文本设置translateChoice{false}即可。由纯字符串数组构造 choices如果你拿到的是一组字符串值需要先映射成带id/name的对象数组const possibleValues [programming, lifestyle, photography]; const ucfirst name name.charAt(0).toUpperCase() name.slice(1); const choices possibleValues.map(value ({ id: value, name: ucfirst(value) })); DualListInput sourceroles choices{choices} /选项来自另一资源交给父组件注入如果需要从另一资源拉取选项你实际上是在编辑一对多或多对多关系。此时应把DualListInput包进ReferenceArrayInput一对多或ReferenceManyToManyInput多对多中无需再传choices——父组件会根据关联资源的可选值自动注入ReferenceArrayInput sourcetag_ids referencetags DualListInput / /ReferenceArrayInput提示如果DualListInput用于ReferenceArrayInput内部translateChoice会被自动置为false因为关联记录文本通常不需要再翻译。关于校验如果需要校验如required()请把validate放在子组件DualListInput上。ReferenceArrayInput本身不接受任何校验属性——这一点在原文档与 ReferenceArrayInput 文档的 Validation 小节 中均有明确说明其原因是通用输入属性应作用于子组件。disableValue自定义禁用字段名默认情况下DualListInput会把choices中disabled字段为真的选项渲染为禁用const choices [ { _id: admin, label: Admin, disabled: true }, { _id: u001, label: Editor }, { _id: u002, label: Moderator }, { _id: u003, label: Reviewer }, ]; DualListInput sourceroles choices{choices} /如果你的数据用其他字段表示禁用例如not_available设置disableValue属性const choices [ { _id: admin, label: Admin, not_available: true }, { _id: u001, label: Editor }, { _id: u002, label: Moderator }, { _id: u003, label: Reviewer }, ]; DualListInput sourceroles choices{choices} disableValuenot_available /optionText自定义选项文本默认用name字段显示选项文本可以通过optionText改为其他字段const choices [ { id: admin, label: Admin }, { id: u001, label: Editor }, { id: u002, label: Moderator }, { id: u003, label: Reviewer }, ]; DualListInput sourceroles choices{choices} optionTextlabel /当选项是来自ReferenceArrayInput或ReferenceManyToManyInput的关联记录时optionText尤其有用。默认情况下 react-admin 使用资源的recordRepresentation函数生成记录标签一旦显式设置了optionText则优先使用它ReferenceArrayInput sourcetag_ids referencetags DualListInput optionTexttag / /ReferenceArrayInput函数形式的 optionTextoptionText也接受函数从而基于整个选项对象拼装文本const choices [ { id: 123, first_name: Leo, last_name: Tolstoi }, { id: 456, first_name: Jane, last_name: Austen }, ]; const optionRenderer choice ${choice.first_name} ${choice.last_name}; DualListInput sourceauthors choices{choices} optionText{optionRenderer} /React 元素形式的 optionTextoptionText还可以是一个 React 元素该元素会在RecordContext中以对应选项作为record渲染因此可以直接在里面使用 Field 组件const choices [ { id: 123, first_name: Leo, last_name: Tolstoi }, { id: 456, first_name: Jane, last_name: Austen }, ]; const FullNameField () { const record useRecordContext(); return span{record.first_name} {record.last_name}/span; } DualListInput sourceauthors choices{choices} optionText{FullNameField /}/optionValue自定义选项值字段默认使用id字段作为写入表单的值可通过optionValue改成其他字段const choices [ { _id: admin, name: Admin }, { _id: u001, name: Editor }, { _id: u002, name: Moderator }, { _id: u003, name: Reviewer }, ]; DualListInput sourceroles choices{choices} optionValue_id /重要限制optionValue仅在choices由choices属性直接提供时生效。当DualListInput位于ReferenceArrayInput内部时optionValue恒为id——因为此时选项是从关联资源拉取的记录而 react-admin 要求记录必须始终具有id字段。translateChoice关闭选项翻译choices默认会被翻译因此可以直接把翻译标识符作为选项文本const choices [ { id: admin, name: myroot.roles.admin }, { id: u001, name: myroot.roles.u001 }, { id: u002, name: myroot.roles.u002 }, { id: u003, name: myroot.roles.u003 }, ];某些场景下你不希望选项被翻译例如选项文本本身就是专有名词或代码将translateChoice设为falseDualListInput sourceroles choices{choices} translateChoice{false}/再次强调当DualListInput是ReferenceArrayInput的子组件时translateChoice会被自动设置为false。在 ReferenceArrayInput 中使用当choices需要由另一资源的记录填充时用ReferenceArrayInput包裹DualListInput并留空choicesimport { Create, DateInput, ReferenceArrayInput, SimpleForm, TextInput, } from react-admin; import { DualListInput } from react-admin/ra-relationships; export const PostCreate () ( Create SimpleForm TextInput sourcetitle / TextInput multiline sourcebody / DateInput sourcepublished_at / ReferenceArrayInput referencetags sourcetags DualListInput optionTextname / /ReferenceArrayInput /SimpleForm /Create );底层数据获取原理从 ReferenceArrayInput 文档 可以了解到这种嵌套方式背后的数据流。ReferenceArrayInput会根据source中的外键数组发起两类查询dataProvider.getMany(tags, { ids: [1, 23, 4] }); dataProvider.getList(tags, { filter: {}, sort: { field: id, order: DESC }, pagination: { page: 1, perPage: 25 } });getMany用于取回当前已选记录渲染右侧已选列表getList用于拉取全部候选记录填充左侧可选列表。正因为候选列表来自一次getList查询ReferenceArrayInput的page、perPage、sort、filter等属性都会间接影响DualListInput看到的可选条目。默认只取前 25 条perPage默认 25如果可选条目很多但你又坚持使用DualListInput务必通过perPage调大拉取数量同时接受它没有自动补全、无法按需搜索的局限。校验放在子组件上需要校验时例如至少选择一项把validate放到DualListInput上而不是ReferenceArrayInput上ReferenceArrayInput sourcetag_ids referencetags DualListInput validate{required()} / /ReferenceArrayInput这也与 Validation 文档 中校验属性作用于子输入组件的约定一致。sxCSS API 与全局主题覆盖DualListInput接受常规的className属性也支持用sx覆盖内部各子元素的样式sx语法与示例参见 SX 文档。可用的样式子类如下Rule nameDescription .RaDualListInput-main主容器 .RaDualListInput-label标签 .RaDualListInput-actions按钮容器 .RaDualListInput-button每个按钮 .RaDualListInput-addButton添加按钮 .RaDualListInput-removeButton移除按钮 .RaDualListInput-list每个列表 .RaDualListInput-listHeader每个列表的标题栏 .RaDualListInput-selectedList已选列表 .RaDualListInput-availableList可选列表例如调整两个列表的固定高度与按钮列布局DualListInput sourceroles choices{choices} sx{{ .RaDualListInput-list: { height: 240 }, .RaDualListInput-availableList: { bgcolor: grey.50 }, .RaDualListInput-selectedList: { bgcolor: primary.light }, }} /若要使用应用级样式覆盖theme overrides一次性作用于所有DualListInput实例请使用RaDualListInput作为组件覆写 key相关机制见 AppTheme 文档的 Theming Individual Components 小节。常见问题与注意事项数组值必须是source上的数组字段表单值形如roles: [u001, u003]DualListInput读写该数组移动操作实时更新数组内容。optionValue在 ReferenceArrayInput 内不生效此时恒为id且要求关联资源记录带id字段。translateChoice在 ReferenceArrayInput 内自动为false。校验只能放子组件ReferenceArrayInput不接受validate。不适合超多候选条目由于没有自动补全当参考资源条目非常多时DualListInput可能不是最佳选择此时可考虑AutocompleteArrayInput自动补全或DataTableInput支持多列比较作为ReferenceArrayInput的子组件。选项禁用只影响移动操作disabled或disableValue指定的字段为真的选项无法被用户加入/移出已选列表。总结DualListInput用最直观的双列穿梭交互解决了数组值与关联关系的编辑问题静态场景下通过choices直接声明可选列表配合optionText、optionValue、disableValue、translateChoice覆盖各种数据形态动态场景下则作为ReferenceArrayInput/ReferenceManyToManyInput的子组件由父组件注入关联记录。借助sx子类与RaDualListInput全局覆写键还能将它的外观完全纳入你的主题体系。在 react-admin 的输入组件体系中它和TextArrayInput、AutocompleteArrayInput、SelectArrayInput、CheckboxGroupInput互为补充按候选集规模与交互需求选择合适的方案即可。【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考