# ColPicker
Use multi-column picker for cascading selection, providing better interaction experience. The multi-column picker supports unlimited level selection.
::: tip Note
Multi-column picker is commonly used for selecting provinces, cities, and districts. We use Vant's China province, city, and district data as the data source. You can install the [@vant/area-data](https://github.com/youzan/vant/tree/main/packages/vant-area-data) npm package to import it:
```bash
# via npm
npm i @vant/area-data
# via yarn
yarn add @vant/area-data
# via pnpm
pnpm add @vant/area-data
# via Bun
bun add @vant/area-data
```
:::
**_To facilitate developers using `@vant/area-data` for development and debugging, we have encapsulated `useColPickerData`. You can directly use `useColPickerData` to get the data source._**
::: details `useColPickerData` wrapper based on @vant/area-data
```typescript
// You can place this code in src/hooks/useColPickerData.ts in your project
import { useCascaderAreaData } from '@vant/area-data'
export type CascaderOption = {
text: string
value: string
children?: CascaderOption[]
}
/**
* Use '@vant/area-data' as data source to construct ColPicker component data
* @returns
*/
export function useColPickerData() {
// '@vant/area-data' data source
const colPickerData: CascaderOption[] = useCascaderAreaData()
// Find child nodes by code, return all nodes if no code provided
function findChildrenByCode(data: CascaderOption[], code?: string): CascaderOption[] | null {
if (!code) {
return data
}
for (const item of data) {
if (item.value === code) {
return item.children || null
}
if (item.children) {
const childrenResult = findChildrenByCode(item.children, code)
if (childrenResult) {
return childrenResult
}
}
}
return null
}
return { colPickerData, findChildrenByCode }
}
```
:::
## Basic Usage
`label` sets the left text content;
`columns` sets the data source, which is a two-dimensional array. Each column is a one-dimensional array, and each option includes `value` (option value) and `label` (option name).
`v-model` sets the selected value, which is an array;
You can also listen to the `change` event to get the selected value. The `event` is an object containing two properties: `value` (selected value array) and `selectedItems` (selected item object array).
Pass in the `column-change` property, which is of type `function` and receives an options object parameter. The structure of options is as follows:
| Parameter | Type | Description | Version |
| --------- | ---- | ----------- | ------- |
| selectedItem | object | Currently selected item in the column, data structure is consistent with the options in columns | - |
| index | number | Current column index | - |
| rowIndex | number | Current column selected item index | - |
| resolve | function | Receives the option array for the next column | - |
| finish | function | End picker selection, if cannot close normally like data fetch failure, execute `finish(false)` | - |
```html
```
```typescript
// useColPickerData can be referenced from the introduction at the top of this section
// Adjust the import path according to your actual situation, don't just copy and paste
import { useColPickerData } from '@/hooks/useColPickerData'
const { colPickerData, findChildrenByCode } = useColPickerData()
const value = ref([])
const area = ref([
colPickerData.map((item) => {
return {
value: item.value,
label: item.text
}
})
])
const columnChange = ({ selectedItem, resolve, finish }) => {
const areaData = findChildrenByCode(colPickerData, selectedItem.value)
if (areaData && areaData.length) {
resolve(
areaData.map((item) => {
return {
value: item.value,
label: item.text
}
})
)
} else {
finish()
}
}
function handleConfirm({ value }) {
console.log(value)
}
```
## Asynchronous Loading
Generally, column-change is an asynchronous data fetching operation. When column-change is triggered, the component will have a default loading state, which is closed after the data responds.
If the asynchronous data request fails, call `finish(false)`.
```html
```
```typescript
// useColPickerData can be referenced from the introduction at the top of this section
// Adjust the import path according to your actual situation, don't just copy and paste
import { useColPickerData } from '@/hooks/useColPickerData'
const { colPickerData, findChildrenByCode } = useColPickerData()
const value = ref([])
const area = ref([
colPickerData.map((item) => {
return {
value: item.value,
label: item.text
}
})
])
const columnChange = ({ selectedItem, resolve, finish }) => {
// Simulate asynchronous request
setTimeout(() => {
// Simulate request failure
if (Math.random() > 0.7) {
finish(false)
toast.error('Data request failed, please try again')
return
}
// Why use selectedItem.value as code? Because when constructing area, we put the identifier in the value field, similarly you can change it to other fields as long as they correspond to area's fields
const areaData = findChildrenByCode(colPickerData, selectedItem.value)
if (areaData && areaData.length) {
resolve(
areaData.map((item) => {
return {
value: item.value,
label: item.text
}
})
)
} else {
// When there are no more items, complete the operation
finish()
}
}, 300)
}
function handleConfirm({ value }) {
console.log(value)
}
```
## Initial Options
There are two ways to set initial options:
1) When setting initial options, the length of the `columns` array should match the length of the `value` array, and each value in `value` must be found in `columns`.
```html
```
```typescript
// useColPickerData can be referenced from the introduction at the top of this section
// Adjust the import path according to your actual situation, don't just copy and paste
import { useColPickerData } from '@/hooks/useColPickerData'
const { colPickerData, findChildrenByCode } = useColPickerData()
const value = ref(['110000', '110100', '110101'])
const area = ref([
colPickerData.map((item) => {
return {
value: item.value,
label: item.text
}
}),
findChildrenByCode(colPickerData, '110000')!.map((item) => {
return {
value: item.value,
label: item.text
}
}),
findChildrenByCode(colPickerData, '110100')!.map((item) => {
return {
value: item.value,
label: item.text
}
})
])
const columnChange = ({ selectedItem, resolve, finish, index }) => {
const areaData = findChildrenByCode(colPickerData, selectedItem.value)
if (areaData && areaData.length) {
resolve(
areaData.map((item) => {
return {
value: item.value,
label: item.text
}
})
)
} else {
finish()
}
}
```
2) Use the `auto-complete` attribute. When `auto-complete` is `true`, the component will automatically trigger the `column-change` event to complete the data.
```html
```
```typescript
// useColPickerData can be referenced from the introduction at the top of this section
// Adjust the import path according to your actual situation, don't just copy and paste
import { useColPickerData } from '@/hooks/useColPickerData'
const { colPickerData, findChildrenByCode } = useColPickerData()
const value = ref(['110000', '110100', '110101'])
const area = ref([
colPickerData.map((item) => {
return {
value: item.value,
label: item.text
}
})
])
const columnChange = ({ selectedItem, resolve, finish }) => {
const areaData = findChildrenByCode(colPickerData, selectedItem.value)
if (areaData && areaData.length) {
resolve(
areaData.map((item) => {
return {
value: item.value,
label: item.text
}
})
)
} else {
finish()
}
}
```
## Disabled
Set `disabled` to disable the picker.
```html
```
## Readonly
Set `readonly` to make the picker readonly.
```html
```
## Disabled Options
Set the `disabled` property in option data to disable specific options.
```html
```
```typescript
const area = ref([
[
{ value: '1', label: 'Beijing', disabled: true },
{ value: '2', label: 'Shanghai' },
{ value: '3', label: 'Shenzhen' }
]
])
const columnChange = ({ selectedItem, resolve, finish }) => {
if (selectedItem.value === '1') {
resolve([
{ value: '11', label: 'Dongcheng District' },
{ value: '12', label: 'Xicheng District' }
])
} else if (selectedItem.value === '2') {
resolve([
{ value: '21', label: 'Huangpu District' },
{ value: '22', label: 'Xuhui District' }
])
} else {
finish()
}
}
```
## Option Tips
Set the `tip` property in option data to show tips for options.
```html
```
```typescript
const area = ref([
[
{ value: '1', label: 'Beijing', tip: 'Capital' },
{ value: '2', label: 'Shanghai', tip: 'Municipality' },
{ value: '3', label: 'Shenzhen', tip: 'Special Economic Zone' }
]
])
const columnChange = ({ selectedItem, resolve, finish }) => {
if (selectedItem.value === '1') {
resolve([
{ value: '11', label: 'Dongcheng District' },
{ value: '12', label: 'Xicheng District' }
])
} else if (selectedItem.value === '2') {
resolve([
{ value: '21', label: 'Huangpu District' },
{ value: '22', label: 'Xuhui District' }
])
} else {
finish()
}
}
```
## Display Format
Set `display-format` to customize the display text.
```html
```
```typescript
const displayFormat = (items) => {
return items.map((item) => item.label).join(' > ')
}
```
## Set Title
Set `title` to customize the popup title.
```html
```
## Before Confirm
Set `before-confirm` to validate before confirming.
```html
```
```typescript
const beforeConfirm = (value, resolve) => {
if (value.length < 3) {
toast.error('Please select a complete address')
resolve(false)
} else {
resolve(true)
}
}
```
## Error State
Set `error` to show error state.
```html
```
## Required Style
Set `required` to show required asterisk.
```html
```
## Required Marker Position
Set `marker-side` to control the position of the required marker.
```html
```
## Picker Size
Set `size` to change picker size.
```html
```
## Right Align Value
Set `align-right` to right-align the picker value.
```html
```
## Custom Picker
Use slots to customize the picker display.
```html
```
```typescript
const selectedItems = ref([])
const displayFormat = (items) => {
return items.map((item) => item.label).join(' > ')
}
const handleConfirm = ({ selectedItems: items }) => {
selectedItems.value = items
}
```
## Attributes
| Attribute | Description | Type | Options | Default | Version |
|-----------|-------------|------|----------|---------|----------|
| v-model | Selected value | array | - | - | - |
| columns | Picker data, 2D array | array | - | - | - |
| value-key | Key for the `value` property in option objects | string | - | value | - |
| label-key | Key for the `label` property in option objects | string | - | label | - |
| tip-key | Key for the `tip` property in option objects | string | - | tip | - |
| title | Popup title | string | - | - | - |
| label | Left-side text label | string | - | - | - |
| placeholder | Placeholder text | string | - | Select | - |
| disabled | Disabled state | boolean | - | false | - |
| readonly | Readonly state | boolean | - | false | - |
| display-format | Custom display text formatting function (returns a string) | function | - | - | - |
| column-change | Function to handle column changes, receives current column's selected item, column index, selected item index, next column data handler resolve, and finish selection function | function | - | - | - |
| size | Picker size | string | large | - | - |
| label-width | Left-side label width | string | - | 33% | - |
| error | Error state (displays value in red) | boolean | - | false | - |
| required | Whether to display the required asterisk | boolean | - | false | - |
| marker-side | Position of the required marker | string | before / after | before | 1.12.0 |
| align-right | Right-align the picker value | boolean | - | false | - |
| before-confirm | Validation function before confirming, receives (value, resolve) parameters, continue execution through resolve, resolve accepts 1 boolean parameter | function | - | - | - |
| loading-color | Loading icon color | string | - | #4D80F0 | - |
| use-default-slot | Set this option when using default slot | boolean | - | false | - |
| use-label-slot | Set this option when using label slot | boolean | - | false | - |
| close-on-click-modal | Whether to close when clicking modal | boolean | - | true | - |
| auto-complete | Automatically trigger column-change event to complete data, triggers column-change when columns is empty array or columns array length is less than value array length | boolean | - | false | - |
| z-index | Popup z-index | number | - | 15 | - |
| safe-area-inset-bottom | Whether to enable bottom safe area adaptation for popup panel (iPhone X type models) | boolean | - | true | - |
| ellipsis | Whether to hide overflow | boolean | - | false | - |
| prop | Form field `model` property name, required when using form validation | string | - | - | - |
| rules | Form validation rules, used with `wd-form` component | `FormItemRule []` | - | `[]` | - |
| lineWidth | Bottom line width in pixels | number | - | - | 1.3.7 |
| lineHeight | Bottom line height in pixels | number | - | - | 1.3.7 |
| root-portal | Whether to detach from the page, used to solve various fixed positioning issues | boolean | - | false | 1.11.0 |
### FormItemRule Data Structure
| Key | Description | Type |
|-----|-------------|------|
| required | Whether it's a required field | `boolean` |
| message | Error message | `string` |
| validator | Validation through function, can return a `Promise` for async validation | `(value, rule) => boolean \| Promise` |
| pattern | Validation through regular expression, regex mismatch indicates validation failure | `RegExp` |
## Option Data Structure
| Key | Description | Type | Required | Version |
|-----|-------------|------|----------|----------|
| value | Option value | string | Yes | - |
| label | Option name | string | Yes | - |
| tip | Option tip | string | No | - |
| disabled | Disable option | boolean | No | - |
## Events
| Event Name | Description | Parameters | Version |
|------------|-------------|------------|----------|
| confirm | Triggered when the last column option is selected | `{ value(option value array), selectedItems(option array) }` | - |
| close | Triggered when close button or overlay is clicked | - | - |
## Methods
| Method Name | Description | Parameters | Version |
|-------------|-------------|------------|----------|
| open | Open picker popup | - | - |
| close | Close picker popup | - | - |
## Slots
| Name | Description | Version |
|------|-------------|----------|
| default | Custom display | - |
| label | Left slot | - |
## External Style Classes
| Class Name | Description | Version |
|------------|-------------|---------|
| custom-class | Root node style | - |
| custom-label-class | Label external custom style | - |
| custom-value-class | Value external custom style | - |