mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
refactor(components)!: [select-v2] value-key is used for object select (#13263)
refactor(components): [select-v2] value-key is used for object select
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
v-model="value"
|
||||
:options="options"
|
||||
placeholder="Please select"
|
||||
value-key="value.name"
|
||||
value-key="name"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import { ref } from 'vue'
|
||||
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
|
||||
|
||||
const value = ref()
|
||||
const value = ref({ name: 'Option 1', test: 'test 0' })
|
||||
const options = Array.from({ length: 1000 }).map((_, idx) => ({
|
||||
value: {
|
||||
name: `Option ${idx + 1}`,
|
||||
|
||||
@@ -272,22 +272,19 @@ describe('Select', () => {
|
||||
it('default value is Object', async () => {
|
||||
const wrapper = createSelect({
|
||||
data: () => ({
|
||||
valueKey: 'value',
|
||||
value: {
|
||||
value: '1',
|
||||
label: 'option_a',
|
||||
},
|
||||
valueKey: 'id',
|
||||
value: { id: 1 },
|
||||
options: [
|
||||
{
|
||||
value: '1',
|
||||
value: { id: 1 },
|
||||
label: 'option_a',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
value: { id: 2 },
|
||||
label: 'option_b',
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
value: { id: 3 },
|
||||
label: 'option_c',
|
||||
},
|
||||
],
|
||||
@@ -298,9 +295,7 @@ describe('Select', () => {
|
||||
expect(wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`).text()).toBe(
|
||||
vm.options[0].label
|
||||
)
|
||||
expect(wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`).text()).toBe(
|
||||
vm.value.label
|
||||
)
|
||||
expect(vm.value).toEqual(vm.options[0].value)
|
||||
})
|
||||
|
||||
it('sync set value and options', async () => {
|
||||
@@ -352,22 +347,19 @@ describe('Select', () => {
|
||||
return {
|
||||
options: [
|
||||
{
|
||||
id: 'id 1',
|
||||
value: 'value 1',
|
||||
value: { id: 1 },
|
||||
label: 'option 1',
|
||||
},
|
||||
{
|
||||
id: 'id 2',
|
||||
value: 'value 2',
|
||||
value: { id: 2 },
|
||||
label: 'option 2',
|
||||
},
|
||||
{
|
||||
id: 'id 3',
|
||||
value: 'value 3',
|
||||
value: { id: 3 },
|
||||
label: 'option 3',
|
||||
},
|
||||
],
|
||||
value: '',
|
||||
value: undefined,
|
||||
valueKey: 'id',
|
||||
}
|
||||
},
|
||||
@@ -378,12 +370,11 @@ describe('Select', () => {
|
||||
const options = getOptions()
|
||||
options[1].click()
|
||||
await nextTick()
|
||||
expect(vm.value).toBe(vm.options[1].id)
|
||||
vm.valueKey = 'value'
|
||||
await nextTick()
|
||||
expect(vm.value).toEqual(vm.options[1].value)
|
||||
|
||||
options[2].click()
|
||||
await nextTick()
|
||||
expect(vm.value).toBe(vm.options[2].value)
|
||||
expect(vm.value).toEqual(vm.options[2].value)
|
||||
})
|
||||
|
||||
it('disabled option', async () => {
|
||||
@@ -616,18 +607,15 @@ describe('Select', () => {
|
||||
return {
|
||||
options: [
|
||||
{
|
||||
id: 'id 1',
|
||||
value: 'value 1',
|
||||
value: { id: 1, value: 'a' },
|
||||
label: 'option 1',
|
||||
},
|
||||
{
|
||||
id: 'id 2',
|
||||
value: 'value 2',
|
||||
value: { id: 2, value: 'b' },
|
||||
label: 'option 2',
|
||||
},
|
||||
{
|
||||
id: 'id 3',
|
||||
value: 'value 3',
|
||||
value: { id: 3, value: 'c' },
|
||||
label: 'option 3',
|
||||
},
|
||||
],
|
||||
@@ -644,13 +632,24 @@ describe('Select', () => {
|
||||
options[1].click()
|
||||
await nextTick()
|
||||
expect(vm.value.length).toBe(1)
|
||||
expect(vm.value[0]).toBe(vm.options[1].id)
|
||||
vm.valueKey = 'value'
|
||||
await nextTick()
|
||||
expect(vm.value).toContainEqual(vm.options[1].value)
|
||||
options[2].click()
|
||||
await nextTick()
|
||||
expect(vm.value.length).toBe(2)
|
||||
expect(vm.value[1]).toBe(vm.options[2].value)
|
||||
expect(vm.value).toContainEqual(vm.options[2].value)
|
||||
options[2].click()
|
||||
await nextTick()
|
||||
expect(vm.value.length).toBe(1)
|
||||
expect(vm.value).not.toContainEqual(vm.options[2].value)
|
||||
|
||||
vm.valueKey = 'value'
|
||||
await nextTick()
|
||||
expect(vm.value.length).toBe(1)
|
||||
expect(vm.value).toContainEqual(vm.options[1].value)
|
||||
options[0].click()
|
||||
await nextTick()
|
||||
expect(vm.value.length).toBe(2)
|
||||
expect(vm.value).toContainEqual(vm.options[0].value)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { computed, defineComponent, inject, ref, unref, watch } from 'vue'
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
inject,
|
||||
ref,
|
||||
toRaw,
|
||||
unref,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { get } from 'lodash-unified'
|
||||
import { isObject, isUndefined } from '@element-plus/utils'
|
||||
import {
|
||||
@@ -69,7 +77,7 @@ export default defineComponent({
|
||||
return (
|
||||
arr &&
|
||||
arr.some((item) => {
|
||||
return get(item, valueKey) === get(target, valueKey)
|
||||
return toRaw(get(item, valueKey)) === get(target, valueKey)
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -83,11 +91,10 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const isItemSelected = (modelValue: any[] | any, target: Option) => {
|
||||
const { valueKey } = select.props
|
||||
if (select.props.multiple) {
|
||||
return contains(modelValue, get(target, valueKey))
|
||||
return contains(modelValue, target.value)
|
||||
}
|
||||
return isEqual(modelValue, get(target, valueKey))
|
||||
return isEqual(modelValue, target.value)
|
||||
}
|
||||
|
||||
const isItemDisabled = (modelValue: any[] | any, selected: boolean) => {
|
||||
|
||||
@@ -176,7 +176,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
const valueMap = new Map()
|
||||
|
||||
filteredOptions.value.forEach((option, index) => {
|
||||
valueMap.set(getValueKey(option), { option, index })
|
||||
valueMap.set(getValueKey(option.value), { option, index })
|
||||
})
|
||||
return valueMap
|
||||
})
|
||||
@@ -409,7 +409,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
if (props.multiple) {
|
||||
let selectedOptions = (props.modelValue as any[]).slice()
|
||||
|
||||
const index = getValueIndex(selectedOptions, getValueKey(option))
|
||||
const index = getValueIndex(selectedOptions, option.value)
|
||||
if (index > -1) {
|
||||
selectedOptions = [
|
||||
...selectedOptions.slice(0, index),
|
||||
@@ -421,7 +421,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
props.multipleLimit <= 0 ||
|
||||
selectedOptions.length < props.multipleLimit
|
||||
) {
|
||||
selectedOptions = [...selectedOptions, getValueKey(option)]
|
||||
selectedOptions = [...selectedOptions, option.value]
|
||||
states.cachedOptions.push(option)
|
||||
selectNewOption(option)
|
||||
updateHoveringIndex(idx)
|
||||
@@ -445,7 +445,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
} else {
|
||||
selectedIndex.value = idx
|
||||
states.selectedLabel = option.label
|
||||
update(getValueKey(option))
|
||||
update(option.value)
|
||||
expanded.value = false
|
||||
states.isComposing = false
|
||||
states.isSilentBlur = byClick
|
||||
@@ -457,20 +457,21 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
}
|
||||
}
|
||||
|
||||
const deleteTag = (event: MouseEvent, tag: Option) => {
|
||||
const { valueKey } = props
|
||||
const index = (props.modelValue as Array<any>).indexOf(get(tag, valueKey))
|
||||
const deleteTag = (event: MouseEvent, option: Option) => {
|
||||
let selectedOptions = (props.modelValue as any[]).slice()
|
||||
|
||||
const index = getValueIndex(selectedOptions, option.value)
|
||||
|
||||
if (index > -1 && !selectDisabled.value) {
|
||||
const value = [
|
||||
selectedOptions = [
|
||||
...(props.modelValue as Array<unknown>).slice(0, index),
|
||||
...(props.modelValue as Array<unknown>).slice(index + 1),
|
||||
]
|
||||
states.cachedOptions.splice(index, 1)
|
||||
update(value)
|
||||
emit('remove-tag', get(tag, valueKey))
|
||||
update(selectedOptions)
|
||||
emit('remove-tag', option.value)
|
||||
states.softFocus = true
|
||||
removeNewOption(tag)
|
||||
removeNewOption(option)
|
||||
return nextTick(focusAndUpdatePopup)
|
||||
}
|
||||
event.stopPropagation()
|
||||
@@ -673,8 +674,12 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
states.previousValue = props.modelValue.toString()
|
||||
|
||||
for (const value of props.modelValue) {
|
||||
if (filteredOptionsValueMap.value.has(value)) {
|
||||
const { index, option } = filteredOptionsValueMap.value.get(value)
|
||||
const selectValue = getValueKey(value)
|
||||
|
||||
if (filteredOptionsValueMap.value.has(selectValue)) {
|
||||
const { index, option } =
|
||||
filteredOptionsValueMap.value.get(selectValue)
|
||||
|
||||
states.cachedOptions.push(option)
|
||||
if (!initHovering) {
|
||||
updateHoveringIndex(index)
|
||||
@@ -691,7 +696,8 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
states.previousValue = props.modelValue
|
||||
const options = filteredOptions.value
|
||||
const selectedItemIndex = options.findIndex(
|
||||
(option) => getValueKey(option) === getValueKey(props.modelValue)
|
||||
(option) =>
|
||||
getValueKey(option.value) === getValueKey(props.modelValue)
|
||||
)
|
||||
if (~selectedItemIndex) {
|
||||
states.selectedLabel = options[selectedItemIndex].label
|
||||
|
||||
Reference in New Issue
Block a user