Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v-model with reduce if value changed outside v-select #855

Closed
fretfan opened this issue Apr 24, 2019 · 23 comments
Closed

v-model with reduce if value changed outside v-select #855

fretfan opened this issue Apr 24, 2019 · 23 comments
Assignees
Labels

Comments

@fretfan
Copy link

fretfan commented Apr 24, 2019

Describe the bug
When using v-model with reduce and changing v-model value in vue component v-select value is not updated. Input event not emitted.

To Reproduce
Pick any value from dropdown
Press reset button
https://codepen.io/anon/pen/PgyLXz

<div id="app">
  <h1>Vue Select</h1>
  <v-select v-model="selectedValue" 
            :options="options"
            :reduce="option => option.val">
  </v-select>
  <button @click="resetDropdown">reset</button>
</div>

<script src="https://unpkg.com/vue@latest"></script>
<script src="https://unpkg.com/vue-select@latest"></script>
Vue.component('v-select', VueSelect.VueSelect)

new Vue({
  el: '#app',
  data: {
      selectedValue: null,
      options: [
        { label: "one", val: 1 },
        { label: "two", val: 2 },
        { label: "three", val: 3 }
      ]
  },
   methods: {
    resetDropdown() {
      this.selectedValue = null;
    }
  },
  updated() {
    console.log(this.selectedValue);
  }
})

Expected behavior
V-select value is changed with associated v-model variable.
input event emitted

@sagalbot sagalbot added the bug label Apr 24, 2019
@sagalbot sagalbot self-assigned this Apr 24, 2019
@sagalbot
Copy link
Owner

Thanks for reporting and providing a repro link. I'll get a patch out for this one.

@sagalbot
Copy link
Owner

Forked: https://codepen.io/anon/pen/zXMNRj#anon-login

Notes:

  • I think a possible solution is to add a watcher to selectedValue computed prop

@sagalbot
Copy link
Owner

sagalbot commented May 1, 2019

Duplicate #842

@edwardsph
Copy link

Do you have a workaround for this issue whilst you are working on a patch?
Thanks

@huypn
Copy link

huypn commented Jun 6, 2019

I have tried to update in code and working
Just add this function in watch

watch:{
      value(){
        this.updateValue(this.findOptionFromReducedValue(this.value) || {})
      }
}

That's all!

Hope @sagalbot help to update it.

@Cryde
Copy link

Cryde commented Jun 13, 2019

Forked: https://codepen.io/anon/pen/zXMNRj#anon-login

Notes:

* I think a possible solution is to add a watcher to `selectedValue` computed prop

I've try to do that but with no success.
Any comment on the @huypn patch ?

@wazum
Copy link

wazum commented Jun 18, 2019

Any update on this? This is kind of a blocker right now. The proposal by huypn didn't solve it for me, made it worse by triggering watchers again.

@marshallswain
Copy link

the best workaround I've found for this issue is to not use the reduce attribute and change the v-model bound attribute to be a computed property that does the necessary conversion. Here's an example that I implemented in my project:

import { keyBy } from 'lodash'

export default {
  data: () => ({
    selectedTagIds: []
  }),
  computed: {
    selectedTags: {
      get() {
        // optimize with lodash.keyBy to only loop through data once
        const tagsById = keyBy(this.tags, '_id')
        return this.selectedTagIds.map(id => tagsById[id])
      },
      set(tags) {
        this.selectedTagIds = tags.map(tag => tag._id)
      }
    }
  }
}

@edwardsph

@marshallswain
Copy link

marshallswain commented Jun 24, 2019

Here's a nice little utility version:

// compute-objects-to-ids.js
import { keyBy, get, set } from 'lodash'

export default function computeObjectsToIds({ source, dest, key }) {
  return {
    get() {
      const byId = keyBy(get(this, source), i => get(i, key))
      const destProp = get(this, dest)
      if (Array.isArray(destProp)) {
        return destProp.map(i => byId[i])
      } else {
        return []
      }
    },
    set(items) {
      set(this, dest, items.map(i => get(i, key)))
    }
  }
}

And you use it like this:

import computeObjectsToIds from './compute-objects-to-ids'

export default {
  data: () => ({
    selectedTagIds: []
  }),
  computed: {
    selectedTags: computeObjectsToIds({
      source: 'tags',
      dest: 'selectedTagIds',
      key: '_id'
    }),
  }
}

Once you've got it setup, only pass the array of objects to the v-model of vue-select.

Note that it's using lodash.get and lodash.set, internally, so you can set all of the values to any nested prop available on this. If you want to set on an object in data which contains a tagIds attribute, use dest: 'myObject.tagIds'.

@LRWeber
Copy link

LRWeber commented Jun 26, 2019

I'm eager to see this patched as well! This is an unfortunate bug on an otherwise extremely useful feature.

@rajilesh
Copy link

rajilesh commented Jun 28, 2019

I think one quick fix is for me is, I have created options in beforeMount callback instead in mounted callback

@borekl
Copy link

borekl commented Jul 10, 2019

@rajilesh I am populating my select in created() hook (which should come before beforeMount()) and it doesn't work anyway.

@talkhabi
Copy link

I don't know why this problem has not been resolved so far, but we can solve it simply by setting a key to component

  <v-select
    v-model="selected"
    :options="options"
    :key="selected"
    :reduce="op => op.id"
  />
new Vue({
  el: '#app',
  data: {
    selected: 3,
    options: [
      { id: 1, label: 'op-1' },
      { id: 2, label: 'op-2' },
      { id: 3, label: 'op-3' },
      { id: 4, label: 'op-4' },
      { id: 5, label: 'op-5' }
    ]
  }
})

@borekl
Copy link

borekl commented Jul 11, 2019

@talkhabi Thank you, it works like a charm!

@LRWeber
Copy link

LRWeber commented Jul 11, 2019

The :key fix works great for single selects!

However, it seems to have some unintended side-effects on multiple selects. Specifically, I noticed that the :close-on-select="false" property doesn't work anymore.
See https://codepen.io/anon/pen/orVdYe

@borekl
Copy link

borekl commented Jul 11, 2019

To add to what @LRWeber writes: With multiple selects there's also the problem that Vue complains about key not being a simple type (it's array).

@LRWeber
Copy link

LRWeber commented Jul 11, 2019

@borekl Good catch! I've now managed to resolve that error using :key="selected.join('-')". (Though the side effects still persist.)

@geri777
Copy link

geri777 commented Jul 25, 2019

3 months now... :-(

@LaundroMat
Copy link

@geri777 You probably didn't see this announcement yet.

@doits
Copy link
Contributor

doits commented Jul 27, 2019

I got a fix in #914 which in tracks the value property and updates the internal representation correctly. It does not use updateValue because there should be no input event in this case.

In addition it makes sure the correct option is selected. See #914 for more information.

@sagalbot
Copy link
Owner

Thanks @doits for the great PR! Getting a couple other PRs merged and will tag a release.

@smakintel
Copy link

<v-select v-model="selectedCompanyId" label="Select Company" dense outlined :key="selectedCompanyId" :items="companies" item-text="companyName" item-value="companyId" > </v-select>

declare below under data : {}

`declare below in data

companies: [
{ companyId: '1', companyName: 'One' },
{ companyId: '2', companyName: 'Two' },
{ companyId: '3', companyName: 'Three' }
]`

@kostia7alania
Copy link

its also repeated with i18n :

if set lang - vue-select didn't update lang (val)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests