CascaderSelect 级联选择

如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。

安装方法

  1. 在命令行中执行以下命令npm install @alifd/next@latest -S

开发指南

何时使用

级联选择由选择器和级联组成。把级联组件以弹层的方式隐藏,多用于表单场景。

API

CascaderSelect

参数说明类型默认值
size选择框大小可选值:'small', 'medium', 'large'Enum'medium'
placeholder选择框占位符String-
disabled是否禁用Booleanfalse
hasArrow是否有下拉箭头Booleantrue
hasBorder是否有边框Booleantrue
hasClear是否有清除按钮Booleanfalse
label自定义内联 labelReactNode-
readOnly是否只读,只读模式下可以展开弹层但不能选Boolean-
dataSource数据源,结构可参考下方说明Array<Object>[]
defaultValue(非受控)默认值String/Array<String>null
value(受控)当前值String/Array<String>-
onChange选中值改变时触发的回调函数签名:Function(value: String/Array, data: Object/Array, extra: Object) => void参数:value: {String/Array} 选中的值,单选时返回单个值,多选时返回数组data: {Object/Array} 选中的数据,包括 value 和 label,单选时返回单个值,多选时返回数组,父子节点选中关联时,同时选中,只返回父节点extra: {Object} 额外参数extra.selectedPath: {Array} 单选时选中的数据的路径extra.checked: {Boolean} 多选时当前的操作是选中还是取消选中extra.currentData: {Object} 多选时当前操作的数据extra.checkedData: {Array} 多选时所有被选中的数据extra.indeterminateData: {Array} 多选时半选的数据Function-
defaultExpandedValue默认展开值,如果不设置,组件内部会根据 defaultValue/value 进行自动设置Array<String>-
expandTriggerType展开触发的方式可选值:'click', 'hover'Enum'click'
useVirtual是否开启虚拟滚动Booleanfalse
multiple是否多选Booleanfalse
changeOnSelect是否选中即发生改变, 该属性仅在单选模式下有效Booleanfalse
canOnlyCheckLeaf是否只能勾选叶子项的checkbox,该属性仅在多选模式下有效Booleanfalse
checkStrictly父子节点是否选中不关联Booleanfalse
listStyle每列列表样式对象Object-
listClassName每列列表类名String-
displayRender选择框单选时展示结果的自定义渲染函数签名:Function(label: Array) => ReactNode参数:label: {Array} 选中路径的文本数组返回值:{ReactNode} 渲染在选择框中的内容Function单选时:labelPath => labelPath.join(' / ');多选时:labelPath => labelPathlabelPath.length - 1
itemRender渲染 item 内容的方法签名:Function(item: Object) => ReactNode参数:item: {Object} 渲染节点的item返回值:{ReactNode} item nodeFunction-
showSearch是否显示搜索框Booleanfalse
filter自定义搜索函数签名:Function(searchValue: String, path: Array) => Boolean参数:searchValue: {String} 搜索的关键字path: {Array} 节点路径返回值:{Boolean} 是否匹配Function根据路径所有节点的文本值模糊匹配
resultRender搜索结果自定义渲染函数签名:Function(searchValue: String, path: Array) => ReactNode参数:searchValue: {String} 搜索的关键字path: {Array} 匹配到的节点路径返回值:{ReactNode} 渲染的内容Function按照节点文本 a / b / c 的模式渲染
resultAutoWidth搜索结果列表是否和选择框等宽Booleantrue
notFoundContent无数据时显示内容ReactNode'Not Found'
loadData异步加载数据函数签名:Function(data: Object) => void参数:data: {Object} 当前点击异步加载的数据Function-
header自定义下拉框头部ReactNode-
footer自定义下拉框底部ReactNode-
defaultVisible初始下拉框是否显示Booleanfalse
visible当前下拉框是否显示Boolean-
onVisibleChange下拉框显示或关闭时触发事件的回调函数签名:Function(visible: Boolean, type: String) => void参数:visible: {Boolean} 是否显示type: {String} 触发显示关闭的操作类型, fromTrigger 表示由trigger的点击触发; docClick 表示由document的点击触发Function() => {}
popupStyle下拉框自定义样式对象Object-
popupClassName下拉框样式自定义类名String-
popupContainer下拉框挂载的容器节点String/Function-
popupProps透传到 Popup 的属性对象Object{}
followTrigger是否跟随滚动Boolean-
<!— api-extra-start —>### dataSource数据结构#
  1. const dataSource = [{ value: '2974', label: '西安', children: [ { value: '2975', label: '西安市', disabled: true }, { value: '2976', label: '高陵县', checkboxDisabled: true }, { value: '2977', label: '蓝田县' }, { value: '2978', label: '户县' }, { value: '2979', label: '周至县' }, { value: '4208', label: '灞桥区' }, { value: '4209', label: '长安区' }, { value: '4210', label: '莲湖区' }, { value: '4211', label: '临潼区' }, { value: '4212', label: '未央区' }, { value: '4213', label: '新城区' }, { value: '4214', label: '阎良区' }, { value: '4215', label: '雁塔区' }, { value: '4388', label: '碑林区' }, { value: '610127', label: '其它区' } ]}];
数组中 Item 的自定义属性也会被透传到 onChange 函数的 data 参数中。<!— api-extra-end —>## ARIA and KeyBoard#
按键说明
Up Arrow获取同级当前项前一项焦点
Down Arrow获取同级当前项后一项焦点
Left Arrow进入当前项的子元素,并获取第一个子元素为焦点
Right Arrow返回当前项的父元素并获取焦点
Enter打开目录或选择当前项
Esc关闭目录
SPACE选择当前项

代码示例

基本使用

展示基本的单选用法。

CascaderSelect 级联选择 - 图1

查看源码在线预览

  1. import { CascaderSelect } from '@alifd/next';
  2. class Demo extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. data: []
  7. };
  8. this.handleChange = this.handleChange.bind(this);
  9. }
  10. componentDidMount() {
  11. fetch('https://os.alipayobjects.com/rmsportal/ODDwqcDFTLAguOvWEolX.json')
  12. .then(response => response.json())
  13. .then(data => {
  14. data[1].disabled = true;
  15. this.setState({ data });
  16. })
  17. .catch(e => console.log(e));
  18. }
  19. handleChange(value, data, extra) {
  20. console.log(value, data, extra);
  21. }
  22. render() {
  23. return <CascaderSelect style={{ width: '302px' }} dataSource={this.state.data} onChange={this.handleChange} />;
  24. }
  25. }
  26. ReactDOM.render(<Demo />, mountNode);

展开触发行为

展示可通过 expandTriggerType 来设置不同的展开触发行为,支持 click 和 hover,默认值为 click。

CascaderSelect 级联选择 - 图2

查看源码在线预览

  1. import { Radio, CascaderSelect } from '@alifd/next';
  2. const RadioGroup = Radio.Group;
  3. class Demo extends React.Component {
  4. constructor(props) {
  5. super(props);
  6. this.state = {
  7. triggerType: 'click',
  8. data: []
  9. };
  10. this.handleChange = this.handleChange.bind(this);
  11. this.handleTriggerTypeChange = this.handleTriggerTypeChange.bind(this);
  12. }
  13. componentDidMount() {
  14. fetch('https://os.alipayobjects.com/rmsportal/ODDwqcDFTLAguOvWEolX.json')
  15. .then(response => response.json())
  16. .then(data => this.setState({ data }))
  17. .catch(e => console.log(e));
  18. }
  19. handleChange(value, data, extra) {
  20. console.log(value, data, extra);
  21. }
  22. handleTriggerTypeChange(triggerType) {
  23. this.setState({
  24. triggerType
  25. });
  26. }
  27. render() {
  28. return (
  29. <div>
  30. <div className="trigger-check">
  31. Expand trigger type:
  32. <RadioGroup dataSource={['click', 'hover']} value={this.state.triggerType} onChange={this.handleTriggerTypeChange} />
  33. </div>
  34. <CascaderSelect style={{ width: '302px' }} expandTriggerType={this.state.triggerType} dataSource={this.state.data} onChange={this.handleChange} />
  35. </div>
  36. );
  37. }
  38. }
  39. ReactDOM.render(<Demo />, mountNode);
  1. .trigger-check {
  2. margin-bottom: 10px;
  3. font-size: 14px;
  4. color: #666;
  5. }
  6. .trigger-check .next-radio-group {
  7. margin-left: 10px;
  8. }

多选

展示基本的多选用法。

CascaderSelect 级联选择 - 图3

查看源码在线预览

  1. import { CascaderSelect } from '@alifd/next';
  2. class Demo extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. data: []
  7. };
  8. this.handleChange = this.handleChange.bind(this);
  9. }
  10. componentDidMount() {
  11. fetch('https://os.alipayobjects.com/rmsportal/ODDwqcDFTLAguOvWEolX.json')
  12. .then(response => response.json())
  13. .then(data => {
  14. data[1].disabled = true;
  15. data[2].checkboxDisabled = true;
  16. this.setState({ data });
  17. })
  18. .catch(e => console.log(e));
  19. }
  20. handleChange(value, data, extra) {
  21. console.log(value, data, extra);
  22. }
  23. render() {
  24. return <CascaderSelect style={{ width: '302px' }} multiple dataSource={this.state.data} onChange={this.handleChange} />;
  25. }
  26. }
  27. ReactDOM.render(<Demo />, mountNode);
  1. .cascader-value {
  2. width: 500px;
  3. margin-bottom: 10px;
  4. font-size: 14px;
  5. color: #666;
  6. }

设置是否只能选择叶子项

展示受控单选以及是否选择即改变。

CascaderSelect 级联选择 - 图4

查看源码在线预览

  1. import { Checkbox, CascaderSelect } from '@alifd/next';
  2. class Demo extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. value: null,
  7. changeOnSelect: false,
  8. data: []
  9. };
  10. this.handleCheck = this.handleCheck.bind(this);
  11. this.handleChange = this.handleChange.bind(this);
  12. }
  13. componentDidMount() {
  14. fetch('https://os.alipayobjects.com/rmsportal/ODDwqcDFTLAguOvWEolX.json')
  15. .then(response => response.json())
  16. .then(data => this.setState({ data, value: '2975' }))
  17. .catch(e => console.log(e));
  18. }
  19. handleCheck() {
  20. this.setState({
  21. changeOnSelect: !this.state.changeOnSelect,
  22. value: null
  23. });
  24. }
  25. handleChange(value, data, extra) {
  26. console.log(value, data, extra);
  27. this.setState({
  28. value
  29. });
  30. }
  31. render() {
  32. return (
  33. <div className="control-single-demo">
  34. <label className="change-check">
  35. <Checkbox value={!this.state.changeOnSelect} onChange={this.handleCheck} />
  36. <span className="change-text">Enable changeOnSelect</span>
  37. </label>
  38. <CascaderSelect style={{ width: '302px' }} changeOnSelect={this.state.changeOnSelect} value={this.state.value} dataSource={this.state.data} onChange={this.handleChange} />
  39. </div>
  40. );
  41. }
  42. }
  43. ReactDOM.render(<Demo />, mountNode);
  1. .control-single-demo .change-check {
  2. display: block;
  3. margin-bottom: 10px;
  4. }
  5. .control-single-demo .change-text {
  6. display: inline-block;
  7. margin-left: 10px;
  8. vertical-align: middle;
  9. color: #666;
  10. font-size: 14px;
  11. }

设置父子节点选中是否关联

展示受控多选以及是否开启严格受控父子节点选中不再关联的用法。

CascaderSelect 级联选择 - 图5

查看源码在线预览

  1. import { Checkbox, CascaderSelect } from '@alifd/next';
  2. class Demo extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. value: [],
  7. data: [],
  8. checkStrictly: false
  9. };
  10. this.handleCheck = this.handleCheck.bind(this);
  11. this.handleChange = this.handleChange.bind(this);
  12. }
  13. componentDidMount() {
  14. fetch('https://os.alipayobjects.com/rmsportal/ODDwqcDFTLAguOvWEolX.json')
  15. .then(response => response.json())
  16. .then(data => this.setState({ data, value: ['2975'] }))
  17. .catch(e => console.log(e));
  18. }
  19. handleCheck() {
  20. this.setState({
  21. checkStrictly: !this.state.checkStrictly,
  22. value: []
  23. });
  24. }
  25. handleChange(value, data, extra) {
  26. console.log(value, data, extra);
  27. this.setState({
  28. value
  29. });
  30. }
  31. render() {
  32. return (
  33. <div className="control-multiple-demo">
  34. <label className="strictly-check">
  35. <Checkbox value={this.state.checkStrictly} onChange={this.handleCheck} />
  36. <span className="strictly-text">Enable checkStrictly</span>
  37. </label>
  38. <CascaderSelect style={{ width: '302px' }} multiple checkStrictly={this.state.checkStrictly} value={this.state.value} dataSource={this.state.data} onChange={this.handleChange} />
  39. </div>
  40. );
  41. }
  42. }
  43. ReactDOM.render(<Demo />, mountNode);
  1. .control-multiple-demo .strictly-check {
  2. display: block;
  3. margin-bottom: 10px;
  4. }
  5. .control-multiple-demo .strictly-text {
  6. display: inline-block;
  7. margin-left: 10px;
  8. vertical-align: middle;
  9. color: #666;
  10. font-size: 14px;
  11. }

搜索

通过设置 showSearch 为 true,可以开启组件的搜索功能。

CascaderSelect 级联选择 - 图6

查看源码在线预览

  1. import { Checkbox, CascaderSelect } from '@alifd/next';
  2. class Demo extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. value: [],
  7. data: [],
  8. multiple: false
  9. };
  10. this.handleCheck = this.handleCheck.bind(this);
  11. this.handleChange = this.handleChange.bind(this);
  12. }
  13. componentDidMount() {
  14. fetch('https://os.alipayobjects.com/rmsportal/ODDwqcDFTLAguOvWEolX.json')
  15. .then(response => response.json())
  16. .then(data => this.setState({ data, value: ['2975'] }))
  17. .catch(e => console.log(e));
  18. }
  19. handleCheck() {
  20. this.setState({
  21. multiple: !this.state.multiple,
  22. value: []
  23. });
  24. }
  25. handleChange(value, data, extra) {
  26. console.log(value, data, extra);
  27. this.setState({
  28. value
  29. });
  30. }
  31. render() {
  32. return (
  33. <div className="search-demo">
  34. <label className="multiple-check">
  35. <Checkbox value={this.state.multiple} onChange={this.handleCheck} />
  36. <span className="multiple-text">Multiple select</span>
  37. </label>
  38. <CascaderSelect style={{ width: '302px' }} showSearch multiple={this.state.multiple} value={this.state.value} dataSource={this.state.data} onChange={this.handleChange} />
  39. </div>
  40. );
  41. }
  42. }
  43. ReactDOM.render(<Demo />, mountNode);
  1. .search-demo .multiple-check {
  2. display: block;
  3. margin-bottom: 10px;
  4. }
  5. .search-demo .multiple-text {
  6. display: inline-block;
  7. margin-left: 10px;
  8. vertical-align: middle;
  9. color: #666;
  10. font-size: 14px;
  11. }

自定义样式

可以通过 displayRender 来定制单选时展示的结果,可以通过 listStyle,listClassName 来定制组件宽高。

CascaderSelect 级联选择 - 图7

查看源码在线预览

  1. import { CascaderSelect, Icon } from '@alifd/next';
  2. const dataSource = [{
  3. value: '2973',
  4. label: '陕西',
  5. children: [{
  6. value: '2974',
  7. label: '西安',
  8. children: [
  9. { value: '2975', label: '西安市' },
  10. { value: '2976', label: '高陵县' }
  11. ]
  12. }, {
  13. value: '2980',
  14. label: '铜川',
  15. children: [
  16. { value: '2981', label: '铜川市' },
  17. { value: '2982', label: '宜君县' }
  18. ]
  19. }]
  20. }, {
  21. value: '3371',
  22. label: '新疆',
  23. children: [{
  24. value: '3430',
  25. label: '巴音郭楞蒙古自治州',
  26. children: [
  27. { value: '3431', label: '库尔勒市' },
  28. { value: '3432', label: '和静县' }
  29. ]
  30. }]
  31. }];
  32. const itemRender = item => {
  33. return (
  34. <span>
  35. <Icon type="account" size="xs" /> {item.label}
  36. </span>
  37. );
  38. };
  39. ReactDOM.render(<CascaderSelect style={{ width: '452px'}} listStyle={{ width: '150px', height: '160px' }} displayRender={labels => labels[labels.length - 1]} defaultValue="3431" dataSource={dataSource} itemRender={itemRender} />, mountNode);

异步加载数据

展示动态获取数据的用法。

CascaderSelect 级联选择 - 图8

查看源码在线预览

  1. import { CascaderSelect } from '@alifd/next';
  2. const dataSource = [{
  3. value: '2973',
  4. label: '陕西'
  5. }];
  6. class Demo extends React.Component {
  7. constructor(props) {
  8. super(props);
  9. this.state = {
  10. dataSource
  11. };
  12. this.onLoadData = this.onLoadData.bind(this);
  13. }
  14. onLoadData(data) {
  15. console.log(data);
  16. return new Promise(resolve => {
  17. setTimeout(() => {
  18. this.setState({
  19. dataSource: [{
  20. value: '2973',
  21. label: '陕西',
  22. children: [{
  23. value: '2974',
  24. label: '西安',
  25. children: [
  26. { value: '2975', label: '西安市', isLeaf: true },
  27. { value: '2976', label: '高陵县', isLeaf: true }
  28. ]
  29. }, {
  30. value: '2980',
  31. label: '铜川',
  32. children: [
  33. { value: '2981', label: '铜川市', isLeaf: true },
  34. { value: '2982', label: '宜君县', isLeaf: true }
  35. ]
  36. }]
  37. }]
  38. }, resolve);
  39. }, 500);
  40. });
  41. }
  42. render() {
  43. return <CascaderSelect style={{ width: '302px' }} dataSource={this.state.dataSource} loadData={this.onLoadData} />;
  44. }
  45. }
  46. ReactDOM.render(<Demo />, mountNode);

渲染 DataSource 中不存在的 value

CascaderSelect 级联选择 - 图9

查看源码在线预览

  1. import { CascaderSelect } from '@alifd/next';
  2. class Demo extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. data: []
  7. };
  8. this.handleChange = this.handleChange.bind(this);
  9. }
  10. componentDidMount() {
  11. fetch('https://os.alipayobjects.com/rmsportal/ODDwqcDFTLAguOvWEolX.json')
  12. .then(response => response.json())
  13. .then(data => {
  14. data[1].disabled = true;
  15. this.setState({ data });
  16. })
  17. .catch(e => console.log(e));
  18. }
  19. handleChange(value, data, extra) {
  20. console.log(value, data, extra);
  21. }
  22. valueRender = (item) => {
  23. if (item.label) {
  24. return item.label; // 正常的item
  25. }
  26. // value在 dataSouce里不存在时渲染。
  27. return item.value === '432988' ? '不存在的' : item.value;
  28. };
  29. render() {
  30. return <CascaderSelect valueRender={this.valueRender} defaultValue="432988" style={{ width: '302px' }} dataSource={this.state.data} onChange={this.handleChange} />;
  31. }
  32. }
  33. ReactDOM.render(<Demo />, mountNode);

无障碍

当聚焦在组件上时,通过aria-labelledby对组件进行描述。关于键盘操作请参考ARIA and KeyBoard

CascaderSelect 级联选择 - 图10

查看源码在线预览

  1. import { CascaderSelect } from '@alifd/next';
  2. const data = [{
  3. value: '0100',
  4. label: '金庸',
  5. children: [
  6. { value: '0101', label: '飞狐外传' },
  7. { value: '0102', label: '雪山飞狐' },
  8. { value: '0103', label: '连城诀' },
  9. { value: '0104', label: '天龙八部' },
  10. { value: '0105', label: '射雕英雄传' },
  11. { value: '0106', label: '白马啸西风' },
  12. { value: '0107', label: '鹿鼎记' },
  13. { value: '0108', label: '笑傲江湖' },
  14. { value: '0109', label: '书剑恩仇录' },
  15. { value: '0110', label: '神雕侠侣' },
  16. { value: '0111', label: '侠客行' },
  17. { value: '0112', label: '倚天屠龙记' },
  18. { value: '0113', label: '碧血剑' },
  19. { value: '0114', label: '鸳鸯刀' }
  20. ]
  21. }, {
  22. value: '0200',
  23. label: '古龙',
  24. children: [
  25. { value: '0201', label: '小李飞刀' },
  26. { value: '0202', label: '绝代双骄' },
  27. { value: '0203', label: '大旗英雄传' },
  28. { value: '0204', label: '英雄无泪' },
  29. { value: '0205', label: '边城浪子' },
  30. { value: '0206', label: '楚留香传奇' },
  31. ],
  32. }, {
  33. children: [
  34. { value: '0301', label: '白发魔女传' },
  35. { value: '0302', label: '七剑下天山' },
  36. { value: '0303', label: '云海玉弓缘' }
  37. ],
  38. value: '0300',
  39. label: '梁羽生'
  40. }];
  41. class Demo extends React.Component {
  42. constructor(props) {
  43. super(props);
  44. this.state = {
  45. label: '',
  46. data: []
  47. };
  48. this.handleChange = this.handleChange.bind(this);
  49. }
  50. componentDidMount() {
  51. this.setState({ data });
  52. }
  53. handleChange(value, data, extra) {
  54. console.log(value, data, extra);
  55. this.setState({
  56. label: extra.selectedPath.map(d => d.label).join(' / '),
  57. });
  58. }
  59. render() {
  60. return (
  61. <div>
  62. <div id="a11y-cascader-select" >CascaderSelect: </div>
  63. <CascaderSelect dataSource={this.state.data} onChange={this.handleChange} listStyle={{ width: '200px', height: '256px' }} aria-labelledby="a11y-cascader-select"/>
  64. </div>
  65. );
  66. }
  67. }
  68. ReactDOM.render(<Demo />, mountNode);
  1. .next-menu-item:focus {
  2. background-color: #F2F3F7;
  3. }

相关区块

CascaderSelect 级联选择 - 图11

暂无相关区块