Commit 9366c9a7729271fe4b6714fdec902f5a0ff78c4e
1 parent
ece49d80
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 | 82 | import FunctionalOptions from './functional-options.vue'; |
| 83 | 83 | |
| 84 | 84 | const prefixCls = 'ivu-select'; |
| 85 | - const optionRegexp = /^i-option$|^Option$/; | |
| 85 | + const optionRegexp = /^i-option$|^Option$/i; | |
| 86 | 86 | const optionGroupRegexp = /option-?group/i; |
| 87 | 87 | |
| 88 | 88 | const findChild = (instance, checkFn) => { |
| ... | ... | @@ -99,7 +99,7 @@ |
| 99 | 99 | const opts = node.componentOptions; |
| 100 | 100 | if (opts && opts.tag.match(optionRegexp)) return [node]; |
| 101 | 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 | 103 | const options = children.reduce( |
| 104 | 104 | (arr, el) => [...arr, ...findOptionsInVNode(el)], [] |
| 105 | 105 | ).filter(Boolean); |
| ... | ... | @@ -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 | 138 | const ANIMATION_TIMEOUT = 300; |
| 127 | 139 | |
| 128 | 140 | export default { |
| ... | ... | @@ -210,8 +222,11 @@ |
| 210 | 222 | this.$on('on-select-selected', this.onOptionClick); |
| 211 | 223 | |
| 212 | 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 | 232 | if (this.values.length > 0 && this.selectOptions.length === 0){ |
| ... | ... | @@ -222,7 +237,7 @@ |
| 222 | 237 | |
| 223 | 238 | return { |
| 224 | 239 | prefixCls: prefixCls, |
| 225 | - values: this.getInitialValue(), | |
| 240 | + values: [], | |
| 226 | 241 | dropDownWidth: 0, |
| 227 | 242 | visible: false, |
| 228 | 243 | focusIndex: -1, |
| ... | ... | @@ -400,8 +415,7 @@ |
| 400 | 415 | getOptionData(value){ |
| 401 | 416 | const option = this.flatOptions.find(({componentOptions}) => componentOptions.propsData.value === value); |
| 402 | 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 | 419 | return { |
| 406 | 420 | value: value, |
| 407 | 421 | label: label, |
| ... | ... | @@ -619,14 +633,12 @@ |
| 619 | 633 | values(now, before){ |
| 620 | 634 | const newValue = JSON.stringify(now); |
| 621 | 635 | const oldValue = JSON.stringify(before); |
| 622 | - const shouldEmitInput = newValue !== oldValue; | |
| 623 | - | |
| 636 | + // v-model is always just the value, event with labelInValue === true | |
| 637 | + const vModelValue = (this.publicValue && this.labelInValue) ? | |
| 638 | + (this.multiple ? this.publicValue.map(({value}) => value) : this.publicValue.value) : | |
| 639 | + this.publicValue; | |
| 640 | + const shouldEmitInput = newValue !== oldValue && vModelValue !== this.value; | |
| 624 | 641 | if (shouldEmitInput) { |
| 625 | - // v-model is always just the value, event with labelInValue === true | |
| 626 | - const vModelValue = this.labelInValue ? | |
| 627 | - (this.multiple ? this.publicValue.map(({value}) => value) | |
| 628 | - : | |
| 629 | - this.publicValue.value) : this.publicValue; | |
| 630 | 642 | this.$emit('input', vModelValue); // to update v-model |
| 631 | 643 | this.$emit('on-change', this.publicValue); |
| 632 | 644 | this.dispatch('FormItem', 'on-form-change', this.publicValue); | ... | ... |
test/unit/specs/select.spec.js
| ... | ... | @@ -47,7 +47,7 @@ describe('Select.vue', () => { |
| 47 | 47 | waitForIt( |
| 48 | 48 | () => { |
| 49 | 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 | 53 | const selectedValueSpan = vm.$el.querySelector('.ivu-select-selected-value'); |
| ... | ... | @@ -121,7 +121,7 @@ describe('Select.vue', () => { |
| 121 | 121 | waitForIt( |
| 122 | 122 | () => { |
| 123 | 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 | 127 | const placeholderSpan = vm.$el.querySelector('.ivu-select-placeholder'); | ... | ... |