Commit 801a9c9d6da5cd275b959dd9c61da376814b0055
Committed by
GitHub
Merge pull request #3739 from SergioCrisostomo/select-patches
Select improvements for edge cases
Showing
2 changed files
with
28 additions
and
16 deletions
Show diff stats
src/components/select/select.vue
@@ -82,7 +82,7 @@ | @@ -82,7 +82,7 @@ | ||
82 | import FunctionalOptions from './functional-options.vue'; | 82 | import FunctionalOptions from './functional-options.vue'; |
83 | 83 | ||
84 | const prefixCls = 'ivu-select'; | 84 | const prefixCls = 'ivu-select'; |
85 | - const optionRegexp = /^i-option$|^Option$/; | 85 | + const optionRegexp = /^i-option$|^Option$/i; |
86 | const optionGroupRegexp = /option-?group/i; | 86 | const optionGroupRegexp = /option-?group/i; |
87 | 87 | ||
88 | const findChild = (instance, checkFn) => { | 88 | const findChild = (instance, checkFn) => { |
@@ -99,7 +99,7 @@ | @@ -99,7 +99,7 @@ | ||
99 | const opts = node.componentOptions; | 99 | const opts = node.componentOptions; |
100 | if (opts && opts.tag.match(optionRegexp)) return [node]; | 100 | if (opts && opts.tag.match(optionRegexp)) return [node]; |
101 | if (!node.children && (!opts || !opts.children)) return []; | 101 | if (!node.children && (!opts || !opts.children)) return []; |
102 | - const children = [...(node.children || []), ...(opts && opts.children || [])]; | 102 | + const children = [...(node.children || []), ...(opts && opts.children || [])]; |
103 | const options = children.reduce( | 103 | const options = children.reduce( |
104 | (arr, el) => [...arr, ...findOptionsInVNode(el)], [] | 104 | (arr, el) => [...arr, ...findOptionsInVNode(el)], [] |
105 | ).filter(Boolean); | 105 | ).filter(Boolean); |
@@ -123,6 +123,18 @@ | @@ -123,6 +123,18 @@ | ||
123 | }; | 123 | }; |
124 | }; | 124 | }; |
125 | 125 | ||
126 | + const getNestedProperty = (obj, path) => { | ||
127 | + const keys = path.split('.'); | ||
128 | + return keys.reduce((o, key) => o && o[key] || null, obj); | ||
129 | + }; | ||
130 | + | ||
131 | + const getOptionLabel = option => { | ||
132 | + const textContent = (option.componentOptions.children || []).reduce((str, child) => str + (child.text || ''), ''); | ||
133 | + const innerHTML = getNestedProperty(option, 'data.domProps.innerHTML'); | ||
134 | + return option.componentOptions.propsData.label || textContent || (typeof innerHTML === 'string' ? innerHTML : ''); | ||
135 | + }; | ||
136 | + | ||
137 | + | ||
126 | const ANIMATION_TIMEOUT = 300; | 138 | const ANIMATION_TIMEOUT = 300; |
127 | 139 | ||
128 | export default { | 140 | export default { |
@@ -210,8 +222,11 @@ | @@ -210,8 +222,11 @@ | ||
210 | this.$on('on-select-selected', this.onOptionClick); | 222 | this.$on('on-select-selected', this.onOptionClick); |
211 | 223 | ||
212 | // set the initial values if there are any | 224 | // set the initial values if there are any |
213 | - if (this.values.length > 0 && !this.remote && this.selectOptions.length > 0){ | ||
214 | - this.values = this.values.map(this.getOptionData).filter(Boolean); | 225 | + if (!this.remote && this.selectOptions.length > 0){ |
226 | + this.values = this.getInitialValue().map(value => { | ||
227 | + if (typeof value !== 'number' && !value) return null; | ||
228 | + return this.getOptionData(value); | ||
229 | + }).filter(Boolean); | ||
215 | } | 230 | } |
216 | 231 | ||
217 | if (this.values.length > 0 && this.selectOptions.length === 0){ | 232 | if (this.values.length > 0 && this.selectOptions.length === 0){ |
@@ -222,7 +237,7 @@ | @@ -222,7 +237,7 @@ | ||
222 | 237 | ||
223 | return { | 238 | return { |
224 | prefixCls: prefixCls, | 239 | prefixCls: prefixCls, |
225 | - values: this.getInitialValue(), | 240 | + values: [], |
226 | dropDownWidth: 0, | 241 | dropDownWidth: 0, |
227 | visible: false, | 242 | visible: false, |
228 | focusIndex: -1, | 243 | focusIndex: -1, |
@@ -400,8 +415,7 @@ | @@ -400,8 +415,7 @@ | ||
400 | getOptionData(value){ | 415 | getOptionData(value){ |
401 | const option = this.flatOptions.find(({componentOptions}) => componentOptions.propsData.value === value); | 416 | const option = this.flatOptions.find(({componentOptions}) => componentOptions.propsData.value === value); |
402 | if (!option) return null; | 417 | if (!option) return null; |
403 | - const textContent = option.componentOptions.children.reduce((str, child) => str + (child.text || ''), ''); | ||
404 | - const label = option.componentOptions.propsData.label || textContent || ''; | 418 | + const label = getOptionLabel(option); |
405 | return { | 419 | return { |
406 | value: value, | 420 | value: value, |
407 | label: label, | 421 | label: label, |
@@ -621,14 +635,12 @@ | @@ -621,14 +635,12 @@ | ||
621 | values(now, before){ | 635 | values(now, before){ |
622 | const newValue = JSON.stringify(now); | 636 | const newValue = JSON.stringify(now); |
623 | const oldValue = JSON.stringify(before); | 637 | const oldValue = JSON.stringify(before); |
624 | - const shouldEmitInput = newValue !== oldValue; | ||
625 | - | 638 | + // v-model is always just the value, event with labelInValue === true |
639 | + const vModelValue = (this.publicValue && this.labelInValue) ? | ||
640 | + (this.multiple ? this.publicValue.map(({value}) => value) : this.publicValue.value) : | ||
641 | + this.publicValue; | ||
642 | + const shouldEmitInput = newValue !== oldValue && vModelValue !== this.value; | ||
626 | if (shouldEmitInput) { | 643 | if (shouldEmitInput) { |
627 | - // v-model is always just the value, event with labelInValue === true | ||
628 | - const vModelValue = this.labelInValue ? | ||
629 | - (this.multiple ? this.publicValue.map(({value}) => value) | ||
630 | - : | ||
631 | - this.publicValue.value) : this.publicValue; | ||
632 | this.$emit('input', vModelValue); // to update v-model | 644 | this.$emit('input', vModelValue); // to update v-model |
633 | this.$emit('on-change', this.publicValue); | 645 | this.$emit('on-change', this.publicValue); |
634 | this.dispatch('FormItem', 'on-form-change', this.publicValue); | 646 | this.dispatch('FormItem', 'on-form-change', this.publicValue); |
test/unit/specs/select.spec.js
@@ -47,7 +47,7 @@ describe('Select.vue', () => { | @@ -47,7 +47,7 @@ describe('Select.vue', () => { | ||
47 | waitForIt( | 47 | waitForIt( |
48 | () => { | 48 | () => { |
49 | const selectedValueSpan = vm.$el.querySelector('.ivu-select-selected-value'); | 49 | const selectedValueSpan = vm.$el.querySelector('.ivu-select-selected-value'); |
50 | - return selectedValueSpan.textContent === 'Bar'; | 50 | + return selectedValueSpan && selectedValueSpan.textContent === 'Bar'; |
51 | }, | 51 | }, |
52 | () => { | 52 | () => { |
53 | const selectedValueSpan = vm.$el.querySelector('.ivu-select-selected-value'); | 53 | const selectedValueSpan = vm.$el.querySelector('.ivu-select-selected-value'); |
@@ -121,7 +121,7 @@ describe('Select.vue', () => { | @@ -121,7 +121,7 @@ describe('Select.vue', () => { | ||
121 | waitForIt( | 121 | waitForIt( |
122 | () => { | 122 | () => { |
123 | const selectedValueSpan = vm.$el.querySelector('.ivu-select-selected-value'); | 123 | const selectedValueSpan = vm.$el.querySelector('.ivu-select-selected-value'); |
124 | - return selectedValueSpan.textContent === 'Bar'; | 124 | + return selectedValueSpan && selectedValueSpan.textContent === 'Bar'; |
125 | }, | 125 | }, |
126 | () => { | 126 | () => { |
127 | const placeholderSpan = vm.$el.querySelector('.ivu-select-placeholder'); | 127 | const placeholderSpan = vm.$el.querySelector('.ivu-select-placeholder'); |