Cascader 级联

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

安装方法

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

开发指南

何时使用

  • 适用于从一组具有关联性的数据集合中进行选择的交互方式。

  • 由于子集目录隐藏,级联是一种节约屏幕空间的有效方法。

  • 级别数因业务需求而定,建议不超过5级。

  • 级联多用于表单场景,可以独立在页面中使用,也可以与其他元素组合使用,如级联选择。

API

Cascader

参数说明类型默认值
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>-
expandedValue(受控)当前展开值Array<String>-
expandTriggerType展开触发的方式可选值:'click', 'hover'Enum'click'
onExpand展开时触发的回调函数签名:Function(expandedValue: Array) => void参数:expandedValue: {Array} 各列展开值的数组Function-
useVirtual是否开启虚拟滚动Booleanfalse
multiple是否多选Booleanfalse
canOnlySelectLeaf单选时是否只能选中叶子节点Booleanfalse
canOnlyCheckLeaf多选时是否只能选中叶子节点Booleanfalse
checkStrictly父子节点是否选中不关联Booleanfalse
listStyle每列列表样式对象Object-
listClassName每列列表类名String-
itemRender每列列表项渲染函数签名:Function(data: Object) => ReactNode参数:data: {Object} 数据返回值:{ReactNode} 列表项内容Functionitem => item.label
loadData异步加载数据函数签名:Function(data: Object, source: Object) => void参数:data: {Object} 当前点击异步加载的数据source: {Object} 当前点击数据Function-
<!— 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#
按键说明
Left Arrow获取同级当前项前一项焦点
Right Arrow获取同级当前项后一项焦点
Tab进入当前项的子元素,并获取第一个子元素为焦点
Esc返回当前项的父元素并获取焦点
SPACE选择当前项

代码示例

基本使用

展示基本的单选用法。

Cascader 级联 - 图1

查看源码在线预览

  1. import { Cascader } from '@alifd/next';
  2. class Demo extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. label: '',
  7. data: []
  8. };
  9. this.handleChange = this.handleChange.bind(this);
  10. }
  11. componentDidMount() {
  12. fetch('https://os.alipayobjects.com/rmsportal/ODDwqcDFTLAguOvWEolX.json')
  13. .then(response => response.json())
  14. .then(data => {
  15. data[1].disabled = 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. this.setState({
  23. label: extra.selectedPath.map(d => d.label).join(' / ')
  24. });
  25. }
  26. render() {
  27. return (
  28. <div>
  29. <div className="cascader-value">Select: {this.state.label}</div>
  30. <Cascader dataSource={this.state.data} onChange={this.handleChange} />
  31. </div>
  32. );
  33. }
  34. }
  35. ReactDOM.render(<Demo />, mountNode);
  1. .cascader-value {
  2. margin-bottom: 10px;
  3. font-size: 14px;
  4. color: #666;
  5. }

展开触发行为

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

Cascader 级联 - 图2

查看源码在线预览

  1. import { Radio, Cascader } 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. <Cascader 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. }

多选

展示基本的多选用法。

Cascader 级联 - 图3

查看源码在线预览

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

设置是否只能选择叶子项

展示受控单选以及是否只能选择叶子项的用法。

Cascader 级联 - 图4

查看源码在线预览

  1. import { Checkbox, Cascader } from '@alifd/next';
  2. class Demo extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. value: null,
  7. canOnlySelectLeaf: 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. canOnlySelectLeaf: !this.state.canOnlySelectLeaf,
  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="leaf-check">
  35. <Checkbox value={this.state.canOnlySelectLeaf} onChange={this.handleCheck} />
  36. <span className="leaf-text">Enable canOnlySelectLeaf</span>
  37. </label>
  38. <Cascader canOnlySelectLeaf={this.state.canOnlySelectLeaf} 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 .leaf-check {
  2. display: block;
  3. margin-bottom: 10px;
  4. }
  5. .control-single-demo .leaf-text {
  6. display: inline-block;
  7. margin-left: 10px;
  8. vertical-align: middle;
  9. color: #666;
  10. font-size: 14px;
  11. }

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

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

Cascader 级联 - 图5

查看源码在线预览

  1. import { Checkbox, Cascader } 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. <Cascader 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. }

自定义样式

可以通过listStylelistClassName来定制组件宽高。

Cascader 级联 - 图6

查看源码在线预览

  1. import { Cascader } 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. ReactDOM.render(<Cascader defaultValue="3439" defaultExpandedValue={['3371', '3430']} listStyle={{ width: '200px', height: '256px' }} dataSource={dataSource} />, mountNode);

异步加载数据

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

Cascader 级联 - 图7

查看源码在线预览

  1. import { Cascader } 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 <Cascader canOnlySelectLeaf dataSource={this.state.dataSource} loadData={this.onLoadData} />;
  44. }
  45. }
  46. ReactDOM.render(<Demo />, mountNode);
  1. .cascader-value {
  2. margin-bottom: 10px;
  3. font-size: 14px;
  4. color: #666;
  5. }

异步加载数据

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

Cascader 级联 - 图8

查看源码在线预览

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

相关区块

Cascader 级联 - 图9

暂无相关区块