Skip to content

Commit 33ca7ee

Browse files
authored
fix(components): [TreeSelect] incorrect label when child not rendered (element-plus#10716)
* feat: add `treeEach` utility function * fix(components): [TreeSelect] incorrect label when child not rendered * docs(components): [TreeSelect] remove tips for resolved issues * fix(components): [TreeSelect] add `cacheData` props for lazy label * docs(components): [TreeSelect] add `cacheData` document and examples * docs(components): [TreeSelect] add version identification for new props * refactor(components): [TreeSelect] replace any type * docs(components): [TreeSelect] update version tag
1 parent fd711d5 commit 33ca7ee

File tree

7 files changed

+149
-11
lines changed

7 files changed

+149
-11
lines changed

docs/en-US/component/tree-select.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,6 @@ tree-select/basic
1818

1919
:::
2020

21-
:::tip
22-
23-
Since `render-after-expand` defaults to `true`,
24-
the selected label name may not be displayed when echoing,
25-
you can set it to `false` to display the correct name.
26-
27-
:::
28-
2921
## Select any level
3022

3123
When using the `check-strictly=true` attribute, any node can be checked,
@@ -107,3 +99,9 @@ and please go to the original component to view the documentation.
10799
| --------------------------------------- | ----------------------------- | ----------------------------------- | ---------------------------------- |
108100
| [tree](./tree.md#attributes) | [tree](./tree.md#method) | [tree](./tree.md#events) | [tree](./tree.md#slots) |
109101
| [select](./select.md#select-attributes) | [select](./select.md#methods) | [select](./select.md#select-events) | [select](./select.md#select-slots) |
102+
103+
### Own Attributes
104+
105+
| Name | Description | Type | Accepted Values | Default |
106+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----- | --------------- | ------- |
107+
| cacheData<VersionTag version="2.2.26" /> | The cached data of the lazy node, the structure is the same as the data, used to get the label of the unloaded data | array |||

docs/examples/tree-select/lazy.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
<template>
22
<el-tree-select v-model="value" lazy :load="load" :props="props" />
3+
<el-divider />
4+
<VersionTag version="2.2.26" /> show lazy load label:
5+
<el-tree-select
6+
v-model="value2"
7+
lazy
8+
:load="load"
9+
:props="props"
10+
:cache-data="cacheData"
11+
/>
312
</template>
413

514
<script lang="ts" setup>
615
import { ref } from 'vue'
716
817
const value = ref()
18+
const value2 = ref(5)
19+
20+
const cacheData = [{ value: 5, label: 'lazy load node5' }]
921
1022
const props = {
1123
label: 'label',

packages/components/tree-select/__tests__/tree-select.test.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,4 +433,45 @@ describe('TreeSelect.vue', () => {
433433
tree.findAll('.el-tree-node__children')[0].attributes('style')
434434
).not.toContain('display: none;')
435435
})
436+
437+
test('show correct label when child options are not rendered', async () => {
438+
const modelValue = ref<number>()
439+
const { select } = createComponent({
440+
props: {
441+
modelValue,
442+
renderAfterExpand: true,
443+
},
444+
})
445+
446+
await nextTick()
447+
expect(select.vm.selectedLabel).toBe('')
448+
449+
modelValue.value = 111
450+
await nextTick()
451+
expect(select.vm.selectedLabel).toBe('三级 1-1')
452+
})
453+
454+
test('show correct label when lazy load', async () => {
455+
const modelValue = ref<number>(1)
456+
const { select } = createComponent({
457+
props: {
458+
data: [],
459+
modelValue,
460+
lazy: true,
461+
load: (node: object, resolve: (p: any) => any[]) => {
462+
resolve([{ value: 2, label: '2-label', isLeaf: true }])
463+
},
464+
cacheData: [{ value: 3, label: '3-label' }],
465+
},
466+
})
467+
468+
// no load & no cache will be default value
469+
await nextTick()
470+
expect(select.vm.selectedLabel).toBe(1)
471+
472+
// no load & has cache will be correct label
473+
modelValue.value = 3
474+
await nextTick()
475+
expect(select.vm.selectedLabel).toBe('3-label')
476+
})
436477
})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { defineComponent, inject } from 'vue'
2+
import { selectKey } from '@element-plus/components/select'
3+
import type { SelectContext } from '@element-plus/components/select'
4+
import type { PropType } from 'vue'
5+
6+
// same as el-option instance,
7+
// these are required for `cachedOptions`
8+
export type CacheOption = {
9+
value: string | number | boolean | object
10+
currentLabel: string | number
11+
isDisabled: boolean
12+
}
13+
14+
export default defineComponent({
15+
props: {
16+
data: {
17+
type: Array as PropType<CacheOption[]>,
18+
default: () => [],
19+
},
20+
},
21+
setup(props) {
22+
const select = inject(selectKey) as NonNullable<SelectContext>
23+
24+
props.data.forEach((item) => select.cachedOptions.set(item.value, item))
25+
26+
return () => undefined
27+
},
28+
})

packages/components/tree-select/src/tree-select.vue

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import ElSelect from '@element-plus/components/select'
66
import ElTree from '@element-plus/components/tree'
77
import { useSelect } from './select'
88
import { useTree } from './tree'
9+
import CacheOptions from './cache-options'
910
1011
export default defineComponent({
1112
name: 'ElTreeSelect',
@@ -14,6 +15,10 @@ export default defineComponent({
1415
props: {
1516
...ElSelect.props,
1617
...ElTree.props,
18+
cacheData: {
19+
type: Array,
20+
default: () => [],
21+
},
1722
},
1823
setup(props, context) {
1924
const { slots, expose } = context
@@ -24,7 +29,11 @@ export default defineComponent({
2429
const key = computed(() => props.nodeKey || props.valueKey || 'value')
2530
2631
const selectProps = useSelect(props, context, { select, tree, key })
27-
const treeProps = useTree(props, context, { select, tree, key })
32+
const { cacheOptions, ...treeProps } = useTree(props, context, {
33+
select,
34+
tree,
35+
key,
36+
})
2837
2938
// expose ElTree/ElSelect methods
3039
const methods = reactive({})
@@ -71,14 +80,16 @@ export default defineComponent({
7180
}),
7281
{
7382
...slots,
74-
default: () =>
83+
default: () => [
84+
h(CacheOptions, { data: cacheOptions.value }),
7585
h(
7686
ElTree,
7787
reactive({
7888
...treeProps,
7989
ref: (ref) => (tree.value = ref),
8090
})
8191
),
92+
],
8293
}
8394
)
8495
},

packages/components/tree-select/src/tree.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
55
import { isFunction } from '@element-plus/utils'
66
import ElTree from '@element-plus/components/tree'
77
import TreeSelectOption from './tree-select-option'
8-
import { isValidArray, isValidValue, toValidArray, treeFind } from './utils'
8+
import {
9+
isValidArray,
10+
isValidValue,
11+
toValidArray,
12+
treeEach,
13+
treeFind,
14+
} from './utils'
15+
import type { CacheOption } from './cache-options'
916
import type { Ref } from 'vue'
1017
import type ElSelect from '@element-plus/components/select'
1118
import type Node from '@element-plus/components/tree/src/model/node'
@@ -80,6 +87,27 @@ export const useTree = (
8087
})
8188
.filter((item) => isValidValue(item))
8289

90+
const cacheOptions = computed(() => {
91+
if (!props.renderAfterExpand && !props.lazy) return []
92+
93+
const options: CacheOption[] = []
94+
95+
treeEach(
96+
props.data.concat(props.cacheData),
97+
(node) => {
98+
const value = getNodeValByProp('value', node)
99+
options.push({
100+
value,
101+
currentLabel: getNodeValByProp('label', node),
102+
isDisabled: getNodeValByProp('disabled', node),
103+
})
104+
},
105+
(data) => getNodeValByProp('children', data)
106+
)
107+
108+
return options
109+
})
110+
83111
return {
84112
...pick(toRefs(props), Object.keys(ElTree.props)),
85113
...attrs,
@@ -190,5 +218,8 @@ export const useTree = (
190218
}
191219
}
192220
},
221+
222+
// else
223+
cacheOptions,
193224
}
194225
}

packages/components/tree-select/src/utils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,20 @@ export function treeFind<T extends TreeNodeData, R>(
5959
}
6060
}
6161
}
62+
63+
export function treeEach<T extends TreeNodeData>(
64+
treeData: T[],
65+
callback: TreeCallback<T, void>,
66+
getChildren: (data: T) => T[],
67+
parent?: T
68+
) {
69+
for (let i = 0; i < treeData.length; i++) {
70+
const data = treeData[i]
71+
callback(data, i, treeData, parent)
72+
73+
const children = getChildren(data)
74+
if (isValidArray(children)) {
75+
treeEach(children, callback, getChildren, data)
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)