Skip to content

Cascader Component ​

Overview ​

The Cascader component is used for selecting data with hierarchical relationships. It can display multi-level classification data structures, allowing users to select level by level, commonly used in scenarios such as region selection and category selection.

Learning Objectives ​

  • Master the basic concepts and usage scenarios of Cascader
  • Learn how to use the basic cascader selector
  • Understand the differences between single and multiple selection modes
  • Master the configuration methods for data structures
  • Learn to use searchable and lazy loading features
  • Understand custom node content and trigger methods
  • Master the complete usage of the API

Basic Usage ​

Basic Cascader Selector ​

The simplest cascader selector usage:

vue
<template>
  <div>
    <el-cascader
      v-model="value"
      :options="options"
      placeholder="Please select"
      @change="handleChange"
    />
    <p>Selected value: {{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref([])
const options = [
  {
    value: 'guide',
    label: 'Guide',
    children: [
      {
        value: 'disciplines',
        label: 'Design Principles',
        children: [
          {
            value: 'consistency',
            label: 'Consistency'
          },
          {
            value: 'feedback',
            label: 'Feedback'
          },
          {
            value: 'efficiency',
            label: 'Efficiency'
          },
          {
            value: 'controllability',
            label: 'Controllability'
          }
        ]
      },
      {
        value: 'navigation',
        label: 'Navigation',
        children: [
          {
            value: 'side-nav',
            label: 'Side Navigation'
          },
          {
            value: 'top-nav',
            label: 'Top Navigation'
          }
        ]
      }
    ]
  },
  {
    value: 'component',
    label: 'Component',
    children: [
      {
        value: 'basic',
        label: 'Basic Components',
        children: [
          {
            value: 'layout',
            label: 'Layout'
          },
          {
            value: 'color',
            label: 'Color'
          },
          {
            value: 'typography',
            label: 'Typography'
          },
          {
            value: 'icon',
            label: 'Icon'
          },
          {
            value: 'button',
            label: 'Button'
          }
        ]
      },
      {
        value: 'form',
        label: 'Form Components',
        children: [
          {
            value: 'radio',
            label: 'Radio'
          },
          {
            value: 'checkbox',
            label: 'Checkbox'
          },
          {
            value: 'input',
            label: 'Input'
          },
          {
            value: 'input-number',
            label: 'InputNumber'
          },
          {
            value: 'select',
            label: 'Select'
          },
          {
            value: 'cascader',
            label: 'Cascader'
          }
        ]
      }
    ]
  }
]

const handleChange = (value) => {
  console.log('Selected value:', value)
}
</script>

Disabled State ​

Use the disabled attribute to disable the cascader selector or specific options:

vue
<template>
  <div>
    <h4>Disabled Cascader</h4>
    <el-cascader
      v-model="value1"
      :options="options"
      disabled
      placeholder="Please select"
    />
    
    <h4>Disabled Specific Options</h4>
    <el-cascader
      v-model="value2"
      :options="optionsWithDisabled"
      placeholder="Please select"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value1 = ref([])
const value2 = ref([])

const options = [
  {
    value: 'beijing',
    label: 'Beijing',
    children: [
      {
        value: 'chaoyang',
        label: 'Chaoyang District'
      },
      {
        value: 'haidian',
        label: 'Haidian District'
      }
    ]
  }
]

const optionsWithDisabled = [
  {
    value: 'beijing',
    label: 'Beijing',
    children: [
      {
        value: 'chaoyang',
        label: 'Chaoyang District'
      },
      {
        value: 'haidian',
        label: 'Haidian District',
        disabled: true
      }
    ]
  },
  {
    value: 'shanghai',
    label: 'Shanghai',
    disabled: true,
    children: [
      {
        value: 'huangpu',
        label: 'Huangpu District'
      }
    ]
  }
]
</script>

Clearable ​

Enable the clear feature with the clearable attribute:

vue
<template>
  <div>
    <el-cascader
      v-model="value"
      :options="options"
      clearable
      placeholder="Please select"
    />
    <p>Selected value: {{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref(['guide', 'disciplines', 'consistency'])
const options = [
  {
    value: 'guide',
    label: 'Guide',
    children: [
      {
        value: 'disciplines',
        label: 'Design Principles',
        children: [
          {
            value: 'consistency',
            label: 'Consistency'
          },
          {
            value: 'feedback',
            label: 'Feedback'
          }
        ]
      }
    ]
  }
]
</script>

Display Only Last Level ​

Control whether to display the complete path with the show-all-levels attribute:

vue
<template>
  <div>
    <h4>Show Complete Path (Default)</h4>
    <el-cascader
      v-model="value1"
      :options="options"
      placeholder="Please select"
    />
    
    <h4>Show Only Last Level</h4>
    <el-cascader
      v-model="value2"
      :options="options"
      :show-all-levels="false"
      placeholder="Please select"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value1 = ref(['guide', 'disciplines', 'consistency'])
const value2 = ref(['guide', 'disciplines', 'feedback'])

const options = [
  {
    value: 'guide',
    label: 'Guide',
    children: [
      {
        value: 'disciplines',
        label: 'Design Principles',
        children: [
          {
            value: 'consistency',
            label: 'Consistency'
          },
          {
            value: 'feedback',
            label: 'Feedback'
          }
        ]
      }
    ]
  }
]
</script>

Multiple Selection Mode ​

Enable multiple selection mode with props.multiple:

vue
<template>
  <div>
    <el-cascader
      v-model="value"
      :options="options"
      :props="{ multiple: true }"
      placeholder="Please select"
      clearable
    />
    <p>Selected values: {{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref([])
const options = [
  {
    value: 'guide',
    label: 'Guide',
    children: [
      {
        value: 'disciplines',
        label: 'Design Principles',
        children: [
          {
            value: 'consistency',
            label: 'Consistency'
          },
          {
            value: 'feedback',
            label: 'Feedback'
          },
          {
            value: 'efficiency',
            label: 'Efficiency'
          }
        ]
      },
      {
        value: 'navigation',
        label: 'Navigation',
        children: [
          {
            value: 'side-nav',
            label: 'Side Navigation'
          },
          {
            value: 'top-nav',
            label: 'Top Navigation'
          }
        ]
      }
    ]
  },
  {
    value: 'component',
    label: 'Component',
    children: [
      {
        value: 'basic',
        label: 'Basic Components',
        children: [
          {
            value: 'button',
            label: 'Button'
          },
          {
            value: 'icon',
            label: 'Icon'
          }
        ]
      }
    ]
  }
]
</script>

Select Any Level Option ​

Allow selecting options at any level with props.checkStrictly:

vue
<template>
  <div>
    <el-cascader
      v-model="value"
      :options="options"
      :props="{ checkStrictly: true }"
      placeholder="Please select"
      clearable
    />
    <p>Selected value: {{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref([])
const options = [
  {
    value: 'guide',
    label: 'Guide',
    children: [
      {
        value: 'disciplines',
        label: 'Design Principles',
        children: [
          {
            value: 'consistency',
            label: 'Consistency'
          },
          {
            value: 'feedback',
            label: 'Feedback'
          }
        ]
      }
    ]
  },
  {
    value: 'component',
    label: 'Component',
    children: [
      {
        value: 'basic',
        label: 'Basic Components',
        children: [
          {
            value: 'button',
            label: 'Button'
          }
        ]
      }
    ]
  }
]
</script>

Dynamic Loading ​

Implement dynamic loading with props.lazy and props.lazyLoad:

vue
<template>
  <div>
    <el-cascader
      v-model="value"
      :props="props"
      placeholder="Please select"
    />
    <p>Selected value: {{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref([])

let id = 0

const props = {
  lazy: true,
  lazyLoad(node, resolve) {
    const { level } = node
    setTimeout(() => {
      const nodes = Array.from({ length: level + 1 })
        .map(() => ({
          value: ++id,
          label: `Option ${id}`,
          leaf: level >= 2
        }))
      resolve(nodes)
    }, 1000)
  }
}
</script>

Searchable ​

Enable search functionality with the filterable attribute:

vue
<template>
  <div>
    <el-cascader
      v-model="value"
      :options="options"
      filterable
      placeholder="Please select or search"
    />
    <p>Selected value: {{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref([])
const options = [
  {
    value: 'beijing',
    label: 'Beijing',
    children: [
      {
        value: 'chaoyang',
        label: 'Chaoyang District',
        children: [
          {
            value: 'datunlu',
            label: 'Datun Road'
          },
          {
            value: 'sanlitun',
            label: 'Sanlitun'
          }
        ]
      },
      {
        value: 'haidian',
        label: 'Haidian District',
        children: [
          {
            value: 'zhongguancun',
            label: 'Zhongguancun'
          },
          {
            value: 'wudaokou',
            label: 'Wudaokou'
          }
        ]
      }
    ]
  },
  {
    value: 'shanghai',
    label: 'Shanghai',
    children: [
      {
        value: 'huangpu',
        label: 'Huangpu District',
        children: [
          {
            value: 'nanjinglu',
            label: 'Nanjing Road'
          },
          {
            value: 'waitan',
            label: 'The Bund'
          }
        ]
      },
      {
        value: 'pudong',
        label: 'Pudong New District',
        children: [
          {
            value: 'lujiazui',
            label: 'Lujiazui'
          },
          {
            value: 'zhangjiang',
            label: 'Zhangjiang'
          }
        ]
      }
    ]
  }
]
</script>

Custom Node Content ​

Use scoped slots to customize node content:

vue
<template>
  <div>
    <el-cascader
      v-model="value"
      :options="options"
      placeholder="Please select"
    >
      <template #default="{ node, data }">
        <span>{{ data.label }}</span>
        <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
      </template>
    </el-cascader>
    <p>Selected value: {{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref([])
const options = [
  {
    value: 'guide',
    label: 'Guide',
    children: [
      {
        value: 'disciplines',
        label: 'Design Principles',
        children: [
          {
            value: 'consistency',
            label: 'Consistency'
          },
          {
            value: 'feedback',
            label: 'Feedback'
          }
        ]
      },
      {
        value: 'navigation',
        label: 'Navigation',
        children: [
          {
            value: 'side-nav',
            label: 'Side Navigation'
          },
          {
            value: 'top-nav',
            label: 'Top Navigation'
          }
        ]
      }
    ]
  }
]
</script>

Cascader Panel ​

Use the el-cascader-panel component to directly display the cascader panel:

vue
<template>
  <div>
    <el-cascader-panel
      v-model="value"
      :options="options"
      @change="handleChange"
    />
    <p>Selected value: {{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref([])
const options = [
  {
    value: 'guide',
    label: 'Guide',
    children: [
      {
        value: 'disciplines',
        label: 'Design Principles',
        children: [
          {
            value: 'consistency',
            label: 'Consistency'
          },
          {
            value: 'feedback',
            label: 'Feedback'
          }
        ]
      }
    ]
  },
  {
    value: 'component',
    label: 'Component',
    children: [
      {
        value: 'basic',
        label: 'Basic Components',
        children: [
          {
            value: 'button',
            label: 'Button'
          }
        ]
      }
    ]
  }
]

const handleChange = (value) => {
  console.log('Selected value:', value)
}
</script>

Size ​

Use the size attribute to set the size of the cascader selector:

vue
<template>
  <div>
    <h4>Large Size</h4>
    <el-cascader
      v-model="value1"
      :options="options"
      size="large"
      placeholder="Please select"
    />
    
    <h4>Default Size</h4>
    <el-cascader
      v-model="value2"
      :options="options"
      placeholder="Please select"
    />
    
    <h4>Small Size</h4>
    <el-cascader
      v-model="value3"
      :options="options"
      size="small"
      placeholder="Please select"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value1 = ref([])
const value2 = ref([])
const value3 = ref([])

const options = [
  {
    value: 'guide',
    label: 'Guide',
    children: [
      {
        value: 'disciplines',
        label: 'Design Principles',
        children: [
          {
            value: 'consistency',
            label: 'Consistency'
          }
        ]
      }
    ]
  }
]
</script>

Practical Application Examples ​

Region Selector ​

vue
<template>
  <div class="region-selector">
    <h3>Region Selector</h3>
    
    <div class="selector-group">
      <div class="selector-item">
        <label>Select Region:</label>
        <el-cascader
          v-model="selectedRegion"
          :options="regionOptions"
          :props="regionProps"
          placeholder="Please select province/city/district"
          filterable
          clearable
          @change="handleRegionChange"
        />
      </div>
      
      <div class="selector-item">
        <label>Multiple Regions:</label>
        <el-cascader
          v-model="selectedMultipleRegions"
          :options="regionOptions"
          :props="multipleRegionProps"
          placeholder="Please select multiple regions"
          filterable
          clearable
          collapse-tags
          collapse-tags-tooltip
        />
      </div>
      
      <div class="selector-item">
        <label>Any Level:</label>
        <el-cascader
          v-model="selectedAnyLevel"
          :options="regionOptions"
          :props="anyLevelProps"
          placeholder="Select any level"
          clearable
        />
      </div>
    </div>
    
    <div class="selection-result">
      <h4>Selection Results</h4>
      <p>Single Region: {{ getRegionName(selectedRegion) }}</p>
      <p>Multiple Regions: {{ getMultipleRegionNames(selectedMultipleRegions) }}</p>
      <p>Any Level: {{ getRegionName(selectedAnyLevel) }}</p>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'

const selectedRegion = ref([])
const selectedMultipleRegions = ref([])
const selectedAnyLevel = ref([])

const regionOptions = [
  {
    value: 'beijing',
    label: 'Beijing',
    children: [
      {
        value: 'chaoyang',
        label: 'Chaoyang District',
        children: [
          { value: 'sanlitun', label: 'Sanlitun Street' },
          { value: 'datunlu', label: 'Datun Road Street' },
          { value: 'wangjing', label: 'Wangjing Street' }
        ]
      },
      {
        value: 'haidian',
        label: 'Haidian District',
        children: [
          { value: 'zhongguancun', label: 'Zhongguancun Street' },
          { value: 'wudaokou', label: 'Wudaokou Street' },
          { value: 'shangdi', label: 'Shangdi Street' }
        ]
      },
      {
        value: 'xicheng',
        label: 'Xicheng District',
        children: [
          { value: 'xinjiekou', label: 'Xinjiekou Street' },
          { value: 'deshengmen', label: 'Deshengmen Street' }
        ]
      }
    ]
  },
  {
    value: 'shanghai',
    label: 'Shanghai',
    children: [
      {
        value: 'huangpu',
        label: 'Huangpu District',
        children: [
          { value: 'nanjinglu', label: 'Nanjing East Road Street' },
          { value: 'waitan', label: 'The Bund Street' },
          { value: 'yuyuan', label: 'Yuyuan Street' }
        ]
      },
      {
        value: 'pudong',
        label: 'Pudong New District',
        children: [
          { value: 'lujiazui', label: 'Lujiazui Street' },
          { value: 'zhangjiang', label: 'Zhangjiang Town' },
          { value: 'jinqiao', label: 'Jinqiao Town' }
        ]
      }
    ]
  },
  {
    value: 'guangdong',
    label: 'Guangdong Province',
    children: [
      {
        value: 'guangzhou',
        label: 'Guangzhou City',
        children: [
          { value: 'tianhe', label: 'Tianhe District' },
          { value: 'yuexiu', label: 'Yuexiu District' },
          { value: 'haizhu', label: 'Haizhu District' }
        ]
      },
      {
        value: 'shenzhen',
        label: 'Shenzhen City',
        children: [
          { value: 'futian', label: 'Futian District' },
          { value: 'nanshan', label: 'Nanshan District' },
          { value: 'luohu', label: 'Luohu District' }
        ]
      }
    ]
  }
]

const regionProps = {
  expandTrigger: 'hover'
}

const multipleRegionProps = {
  multiple: true,
  expandTrigger: 'hover'
}

const anyLevelProps = {
  checkStrictly: true,
  expandTrigger: 'hover'
}

const handleRegionChange = (value) => {
  ElMessage.success(`Selected region: ${getRegionName(value)}`)
}

const getRegionName = (value) => {
  if (!value || !value.length) return 'Not selected'
  
  const findLabel = (options, path, index = 0) => {
    if (index >= path.length) return ''
    
    const option = options.find(opt => opt.value === path[index])
    if (!option) return ''
    
    if (index === path.length - 1) {
      return option.label
    }
    
    const childLabel = findLabel(option.children || [], path, index + 1)
    return childLabel ? `${option.label} / ${childLabel}` : option.label
  }
  
  return findLabel(regionOptions, value)
}

const getMultipleRegionNames = (values) => {
  if (!values || !values.length) return 'Not selected'
  return values.map(value => getRegionName(value)).join('; ')
}
</script>

<style scoped>
.region-selector {
  max-width: 800px;
  padding: 20px;
  border: 1px solid #dcdfe6;
  border-radius: 8px;
}

.selector-group {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.selector-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.selector-item label {
  width: 100px;
  font-weight: 500;
}

.selector-item .el-cascader {
  flex: 1;
  max-width: 400px;
}

.selection-result {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid #e4e7ed;
}

.selection-result h4 {
  margin-bottom: 12px;
  color: #303133;
}

.selection-result p {
  margin: 4px 0;
  color: #606266;
}
</style>

Category Selector ​

vue
<template>
  <div class="category-selector">
    <h3>Product Category Selector</h3>
    
    <div class="selector-container">
      <div class="selector-section">
        <h4>Select Product Category</h4>
        <el-cascader
          v-model="selectedCategory"
          :options="categoryOptions"
          :props="categoryProps"
          placeholder="Please select product category"
          filterable
          clearable
          @change="handleCategoryChange"
        />
      </div>
      
      <div class="selector-section">
        <h4>Multiple Categories (for filtering)</h4>
        <el-cascader
          v-model="selectedMultipleCategories"
          :options="categoryOptions"
          :props="multipleCategoryProps"
          placeholder="Please select multiple categories"
          filterable
          clearable
          collapse-tags
          collapse-tags-tooltip
        />
      </div>
      
      <div class="selector-section">
        <h4>Category Management (any level selectable)</h4>
        <el-cascader
          v-model="selectedManageCategory"
          :options="categoryOptions"
          :props="manageCategoryProps"
          placeholder="Select category to manage"
          clearable
        />
      </div>
    </div>
    
    <div class="category-panel">
      <h4>Category Panel Display</h4>
      <el-cascader-panel
        v-model="panelValue"
        :options="categoryOptions"
        :props="panelProps"
        @change="handlePanelChange"
      />
    </div>
    
    <div class="selection-summary">
      <h4>Selection Results</h4>
      <p>Product Category: {{ getCategoryName(selectedCategory) }}</p>
      <p>Filter Categories: {{ getMultipleCategoryNames(selectedMultipleCategories) }}</p>
      <p>Management Category: {{ getCategoryName(selectedManageCategory) }}</p>
      <p>Panel Selection: {{ getCategoryName(panelValue) }}</p>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const selectedCategory = ref([])
const selectedMultipleCategories = ref([])
const selectedManageCategory = ref([])
const panelValue = ref([])

const categoryOptions = [
  {
    value: 'electronics',
    label: 'Electronics',
    children: [
      {
        value: 'mobile',
        label: 'Mobile Phones',
        children: [
          { value: 'smartphone', label: 'Smartphones' },
          { value: 'feature-phone', label: 'Feature Phones' },
          { value: 'accessories', label: 'Phone Accessories' }
        ]
      },
      {
        value: 'computer',
        label: 'Computers',
        children: [
          { value: 'laptop', label: 'Laptops' },
          { value: 'desktop', label: 'Desktops' },
          { value: 'tablet', label: 'Tablets' },
          { value: 'peripherals', label: 'Computer Peripherals' }
        ]
      },
      {
        value: 'home-appliances',
        label: 'Home Appliances',
        children: [
          { value: 'tv', label: 'TVs' },
          { value: 'refrigerator', label: 'Refrigerators' },
          { value: 'washing-machine', label: 'Washing Machines' },
          { value: 'air-conditioner', label: 'Air Conditioners' }
        ]
      }
    ]
  },
  {
    value: 'clothing',
    label: 'Clothing',
    children: [
      {
        value: 'mens-clothing',
        label: 'Men\'s Clothing',
        children: [
          { value: 'mens-tops', label: 'Tops' },
          { value: 'mens-bottoms', label: 'Bottoms' },
          { value: 'mens-shoes', label: 'Men\'s Shoes' },
          { value: 'mens-accessories', label: 'Men\'s Accessories' }
        ]
      },
      {
        value: 'womens-clothing',
        label: 'Women\'s Clothing',
        children: [
          { value: 'womens-tops', label: 'Tops' },
          { value: 'womens-bottoms', label: 'Bottoms' },
          { value: 'dresses', label: 'Dresses' },
          { value: 'womens-shoes', label: 'Women\'s Shoes' },
          { value: 'womens-accessories', label: 'Women\'s Accessories' }
        ]
      },
      {
        value: 'kids-clothing',
        label: 'Kids\' Clothing',
        children: [
          { value: 'boys-clothing', label: 'Boys\' Clothing' },
          { value: 'girls-clothing', label: 'Girls\' Clothing' },
          { value: 'baby-clothing', label: 'Baby Clothing' },
          { value: 'kids-shoes', label: 'Kids\' Shoes' }
        ]
      }
    ]
  },
  {
    value: 'books',
    label: 'Books & Media',
    children: [
      {
        value: 'books-literature',
        label: 'Literature',
        children: [
          { value: 'fiction', label: 'Fiction' },
          { value: 'poetry', label: 'Poetry & Essays' },
          { value: 'biography', label: 'Biography' }
        ]
      },
      {
        value: 'books-education',
        label: 'Education',
        children: [
          { value: 'textbooks', label: 'Textbooks' },
          { value: 'exam-prep', label: 'Exam Preparation' },
          { value: 'language-learning', label: 'Language Learning' }
        ]
      },
      {
        value: 'audio-video',
        label: 'Audio & Video',
        children: [
          { value: 'music-cd', label: 'Music CDs' },
          { value: 'movie-dvd', label: 'Movie DVDs' },
          { value: 'audiobooks', label: 'Audiobooks' }
        ]
      }
    ]
  }
]

const categoryProps = {
  expandTrigger: 'hover'
}

const multipleCategoryProps = {
  multiple: true,
  expandTrigger: 'hover'
}

const manageCategoryProps = {
  checkStrictly: true,
  expandTrigger: 'hover'
}

const panelProps = {
  expandTrigger: 'hover'
}

const handleCategoryChange = (value) => {
  ElMessage.success(`Selected category: ${getCategoryName(value)}`)
}

const handlePanelChange = (value) => {
  console.log('Panel selection change:', value)
}

const getCategoryName = (value) => {
  if (!value || !value.length) return 'Not selected'
  
  const findLabel = (options, path, index = 0) => {
    if (index >= path.length) return ''
    
    const option = options.find(opt => opt.value === path[index])
    if (!option) return ''
    
    if (index === path.length - 1) {
      return option.label
    }
    
    const childLabel = findLabel(option.children || [], path, index + 1)
    return childLabel ? `${option.label} > ${childLabel}` : option.label
  }
  
  return findLabel(categoryOptions, value)
}

const getMultipleCategoryNames = (values) => {
  if (!values || !values.length) return 'Not selected'
  return values.map(value => getCategoryName(value)).join('; ')
}
</script>

<style scoped>
.category-selector {
  max-width: 1000px;
  padding: 20px;
  border: 1px solid #dcdfe6;
  border-radius: 8px;
}

.selector-container {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-bottom: 30px;
}

.selector-section {
  padding: 16px;
  border: 1px solid #e4e7ed;
  border-radius: 6px;
  background-color: #fafafa;
}

.selector-section h4 {
  margin: 0 0 12px 0;
  color: #303133;
  font-size: 14px;
}

.selector-section .el-cascader {
  width: 100%;
  max-width: 400px;
}

.category-panel {
  margin-bottom: 30px;
}

.category-panel h4 {
  margin-bottom: 16px;
  color: #303133;
}

.selection-summary {
  padding-top: 20px;
  border-top: 1px solid #e4e7ed;
}

.selection-summary h4 {
  margin-bottom: 12px;
  color: #303133;
}

.selection-summary p {
  margin: 6px 0;
  color: #606266;
  line-height: 1.5;
}
</style>

API Documentation ​

Cascader Attributes ​

NameDescriptionTypeDefault
model-value / v-modelbinding valuearray—
optionsdata sourcearray—
propsconfiguration optionsobject—
sizesizeenum—
placeholderplaceholderstringSelect
disabledwhether to disablebooleanfalse
clearablewhether to support clearingbooleanfalse
show-all-levelswhether to display all levels of the selected value in the inputbooleantrue
collapse-tagswhether to collapse tags in multiple selection modebooleanfalse
collapse-tags-tooltipwhether to show all selected tags when mouse hover collapse-tagsbooleanfalse
separatoroption separatorstring' / '
filterablewhether to enable filteringboolean—
filter-methodcustom filtering methodfunction—
debouncedebounce delay when typing filter keyword, in millisecondsnumber300
before-filterhook function before filteringfunction—
popper-classcustom class name for the dropdown menustring—
teleportedwhether the dropdown is teleported to the bodybooleantrue
tag-typetag typeenuminfo
validate-eventwhether to trigger form validationbooleantrue

Cascader Events ​

NameDescriptionType
changetriggers when the binding value changesFunction
expand-changetriggers when expanding options changeFunction
blurtriggers when the component loses focusFunction
focustriggers when the component obtains focusFunction
visible-changetriggers when the dropdown appears/disappearsFunction
remove-tagtriggers when a tag is removed in multiple selection modeFunction

Cascader Slots ​

NameDescription
defaultcustom content for cascader node. The parameter is
emptycontent when there is no matched options

Cascader Methods ​

NameDescriptionType
getCheckedNodesget the currently selected nodeFunction
focusfocus the componentFunction
blurblur the componentFunction

CascaderPanel Attributes ​

NameDescriptionTypeDefault
model-value / v-modelbinding valuearray—
optionsdata sourcearray—
propsconfiguration optionsobject—

CascaderPanel Events ​

NameDescriptionType
changetriggers when the binding value changesFunction
expand-changetriggers when expanding options changeFunction

Props Configuration ​

NameDescriptionTypeDefault
expandTriggertrigger mode for expanding optionsenum'click'
multiplewhether to select multiple valuesbooleanfalse
checkStrictlywhether to check nodes strictly. If false, parent and child nodes are linkedbooleanfalse
emitPathwhen a node is selected, whether to return an array of all parent node valuesbooleantrue
lazywhether to load child nodes dynamicallybooleanfalse
lazyLoadmethod for loading child nodes, only works when lazy is truefunction—
valuespecify which key value of node object is used as the node's valuestring'value'
labelspecify which key value of node object is used as the node's labelstring'label'
childrenspecify which key value of node object is used as the node's childrenstring'children'
disabledspecify which key value of node object is used as the node's disabledstring'disabled'
leafspecify which key value of node object is used as the node's leaf fieldstring'leaf'

Practical Exercises ​

Exercise 1: Dynamic Loading Cascader ​

Create a cascader with dynamic loading support:

vue
<template>
  <div class="dynamic-cascader">
    <h3>Dynamic Loading Cascader</h3>
    <el-cascader
      v-model="value"
      :props="props"
      placeholder="Please select"
      @change="handleChange"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref([])

// Mock data
const mockData = {
  0: [
    { id: 1, name: 'Beijing', hasChildren: true },
    { id: 2, name: 'Shanghai', hasChildren: true },
    { id: 3, name: 'Guangdong', hasChildren: true }
  ],
  1: [
    { id: 11, name: 'Chaoyang District', hasChildren: true },
    { id: 12, name: 'Haidian District', hasChildren: true }
  ],
  2: [
    { id: 21, name: 'Huangpu District', hasChildren: false },
    { id: 22, name: 'Pudong New District', hasChildren: false }
  ],
  3: [
    { id: 31, name: 'Guangzhou City', hasChildren: true },
    { id: 32, name: 'Shenzhen City', hasChildren: true }
  ],
  11: [
    { id: 111, name: 'Sanlitun', hasChildren: false },
    { id: 112, name: 'Wangjing', hasChildren: false }
  ],
  12: [
    { id: 121, name: 'Zhongguancun', hasChildren: false },
    { id: 122, name: 'Wudaokou', hasChildren: false }
  ],
  31: [
    { id: 311, name: 'Tianhe District', hasChildren: false },
    { id: 312, name: 'Yuexiu District', hasChildren: false }
  ],
  32: [
    { id: 321, name: 'Nanshan District', hasChildren: false },
    { id: 322, name: 'Futian District', hasChildren: false }
  ]
}

const props = {
  lazy: true,
  value: 'id',
  label: 'name',
  leaf: (node) => !node.hasChildren,
  lazyLoad(node, resolve) {
    const { level, value } = node
    const key = level === 0 ? 0 : value
    
    setTimeout(() => {
      const data = mockData[key] || []
      resolve(data)
    }, 1000)
  }
}

const handleChange = (value) => {
  console.log('Selected value:', value)
}
</script>

Exercise 2: Custom Search Cascader ​

Create a cascader with custom search logic:

vue
<template>
  <div class="custom-search-cascader">
    <h3>Custom Search Cascader</h3>
    <el-cascader
      v-model="value"
      :options="options"
      :filter-method="filterMethod"
      filterable
      placeholder="Enter keywords to search"
    >
      <template #default="{ node, data }">
        <span>{{ data.label }}</span>
        <span v-if="data.code" class="code">({{ data.code }})</span>
      </template>
    </el-cascader>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref([])

const options = [
  {
    value: 'tech',
    label: 'Technology Department',
    code: 'TECH',
    children: [
      {
        value: 'frontend',
        label: 'Frontend Development',
        code: 'FE',
        children: [
          { value: 'vue', label: 'Vue.js Development', code: 'VUE' },
          { value: 'react', label: 'React Development', code: 'REACT' },
          { value: 'angular', label: 'Angular Development', code: 'NG' }
        ]
      },
      {
        value: 'backend',
        label: 'Backend Development',
        code: 'BE',
        children: [
          { value: 'java', label: 'Java Development', code: 'JAVA' },
          { value: 'python', label: 'Python Development', code: 'PY' },
          { value: 'nodejs', label: 'Node.js Development', code: 'NODE' }
        ]
      }
    ]
  },
  {
    value: 'product',
    label: 'Product Department',
    code: 'PROD',
    children: [
      {
        value: 'design',
        label: 'Product Design',
        code: 'DESIGN',
        children: [
          { value: 'ui', label: 'UI Design', code: 'UI' },
          { value: 'ux', label: 'UX Design', code: 'UX' }
        ]
      },
      {
        value: 'pm',
        label: 'Product Management',
        code: 'PM',
        children: [
          { value: 'strategy', label: 'Product Strategy', code: 'STRATEGY' },
          { value: 'operation', label: 'Product Operations', code: 'OPS' }
        ]
      }
    ]
  }
]

const filterMethod = (node, keyword) => {
  const { data } = node
  const searchText = keyword.toLowerCase()
  
  // Support searching by label or code
  return (
    data.label.toLowerCase().includes(searchText) ||
    (data.code && data.code.toLowerCase().includes(searchText))
  )
}
</script>

<style scoped>
.code {
  color: #909399;
  font-size: 12px;
  margin-left: 8px;
}
</style>

Common Issues ​

1. Data Structure Configuration Issues ​

Problem: Customizing data structure field names

Solution:

javascript
const props = {
  value: 'id',        // Custom value field
  label: 'name',      // Custom label field
  children: 'items',  // Custom children field
  disabled: 'isDisabled', // Custom disabled field
  leaf: 'isLeaf'      // Custom leaf node field
}

2. Dynamic Loading Performance Issues ​

Problem: Frequent requests during dynamic loading

Solution:

javascript
// Add caching mechanism
const cache = new Map()

const lazyLoad = (node, resolve) => {
  const { value } = node
  const cacheKey = value || 'root'
  
  if (cache.has(cacheKey)) {
    resolve(cache.get(cacheKey))
    return
  }
  
  // Execute asynchronous loading
  fetchData(value).then(data => {
    cache.set(cacheKey, data)
    resolve(data)
  })
}

3. Multiple Selection Value Handling ​

Problem: Handling values in multiple selection mode

Solution:

javascript
// Process multiple selection values
const handleMultipleChange = (values) => {
  // values is a two-dimensional array, each element is a path array
  const selectedPaths = values.map(path => {
    return path.join(' > ')
  })
  console.log('Selected paths:', selectedPaths)
}

Best Practices ​

  1. Data Structure Design: Design hierarchical data structures reasonably, avoid excessive depth
  2. Performance Optimization: Use lazy loading and caching mechanisms for large data volumes
  3. User Experience: Provide search functionality and clear options
  4. Accessibility: Support keyboard navigation and screen readers
  5. Error Handling: Properly handle data loading failures
  6. Responsive Design: Provide appropriate interaction experience on mobile devices

Summary ​

The Cascader component is a powerful hierarchical data selection component that supports:

  • Single and multiple selection modes
  • Dynamic loading and lazy loading
  • Custom search and filtering
  • Flexible data structure configuration
  • Rich customization options
  • Good user experience

Mastering the use of the Cascader component can help you build more flexible and efficient hierarchical data selection interfaces.

References ​

Element Plus Study Guide