diff --git a/src/composables/useOptions.js b/src/composables/useOptions.js index 5aa2f46..96f7c6a 100644 --- a/src/composables/useOptions.js +++ b/src/composables/useOptions.js @@ -3,13 +3,16 @@ import normalize from './../utils/normalize' import isObject from './../utils/isObject' import isNullish from './../utils/isNullish' import arraysEqual from './../utils/arraysEqual' +import arrayObjectsEqual from './../utils/arrayObjectsEqual' export default function useOptions (props, context, dependencies) { - const { options, mode, trackBy, limit, hideSelected, createTag, label, - appendNewTag, multipleLabel, object, loading, delay, resolveOnLoad, - minChars, filterResults, clearOnSearch, clearOnSelect, valueProp, - canDeselect, max } = toRefs(props) + const { + options, mode, trackBy, limit, hideSelected, createTag, label, + appendNewTag, multipleLabel, object, loading, delay, resolveOnLoad, + minChars, filterResults, clearOnSearch, clearOnSelect, valueProp, + canDeselect, max + } = toRefs(props) // ============ DEPENDENCIES ============ @@ -425,20 +428,32 @@ export default function useOptions (props, context, dependencies) }, { flush: 'sync' }) watch(extendedOptions, (n, o) => { - if (!extendedOptions.value.length || !currentValue.value || !currentValue.value.length) { + if (!Object.keys(internalValue.value).length) { + initInternalValue() + } + + if (!n.length || !currentValue.value || !currentValue.value.length) { return } let newValue if (mode.value === 'single') { - newValue = extendedOptions.value[extendedOptions.value.map(v=>v[valueProp.value]).indexOf(currentValue.value)] + newValue = n[n.map(v=>v[valueProp.value]).indexOf(currentValue.value)] + + if (JSON.stringify(newValue) === JSON.stringify(internalValue.value)) { + return + } } else { newValue = [] currentValue.value.forEach((val) => { - newValue.push(extendedOptions.value[extendedOptions.value.map(v=>v[valueProp.value]).indexOf(val)]) + newValue.push(n[n.map(v=>v[valueProp.value]).indexOf(val)]) }) + + if (arrayObjectsEqual(newValue, internalValue.value)) { + return + } } // Update both internal and external value if user is using object values diff --git a/src/utils/arrayObjectsEqual.js b/src/utils/arrayObjectsEqual.js new file mode 100644 index 0000000..60b342d --- /dev/null +++ b/src/utils/arrayObjectsEqual.js @@ -0,0 +1,26 @@ +const BreakException = {} + +export default function arrayObjectsEqual (array1, array2) { + let equal = array1.length === array2.length + + if (!equal) { + return equal + } + + try { + array1.every(function(value, index) { + if (JSON.stringify(value) !== JSON.stringify(array2[index])) { + throw BreakException + } + }) + } catch (e) { + /* istanbul ignore else */ + if (e === BreakException) { + equal = false + } else { + throw e + } + } + + return equal +} \ No newline at end of file diff --git a/tests/unit/composables/useOptions.spec.js b/tests/unit/composables/useOptions.spec.js index aa5916c..349305e 100644 --- a/tests/unit/composables/useOptions.spec.js +++ b/tests/unit/composables/useOptions.spec.js @@ -139,7 +139,7 @@ describe('useOptions', () => { expect(select.vm.filteredOptions[2].label).toBe(3) }) - it('should reactively changes label when options has been changed mode=single', async () => { + it('should reactively changes label when options has been changed mode=single, object=false', async () => { let select = createSelect({ value: 'ru', label: 'name', @@ -155,7 +155,23 @@ describe('useOptions', () => { expect(select.find('.multiselect-single-label').html()).toContain('Россия') }) - it('should reactively changes label when options has been changed mode=tags', async () => { + it('should not update value when options changed but did not affect value mode=single, object=false', async () => { + let select = createSelect({ + value: 'ru', + label: 'name', + valueProp: 'code', + options: [{ code: 'au', name: 'Australia' }, { code: 'ru', name: 'Russia' }, { code: 'us', name: 'USA' }], + }) + + select.vm.$parent.props.options = [{ code: 'au', name: 'Australia' }, { code: 'ru', name: 'Russia' }] + + await nextTick() + + expect(select.find('.multiselect-single-label').element).toBeVisible() + expect(select.find('.multiselect-single-label').html()).toContain('Russia') + }) + + it('should reactively changes label when options has been changed mode=tags, object=false', async () => { let select = createSelect({ mode: 'tags', value: ['ru', 'au'], diff --git a/tests/unit/utils/arrayObjectsEqual.spec.js b/tests/unit/utils/arrayObjectsEqual.spec.js new file mode 100644 index 0000000..03c500c --- /dev/null +++ b/tests/unit/utils/arrayObjectsEqual.spec.js @@ -0,0 +1,7 @@ +import arrayObjectsEqual from './../../../src/utils/arrayObjectsEqual' + +describe('arrayObjectsEqual', () => { + it('should return false if arrays are not same length', () => { + expect(arrayObjectsEqual([1],[1,2])).toBe(false) + }) +}) \ No newline at end of file