ogostos
ogostos

Reputation: 1485

How can I connect buttons and select with its options in Vue JS

I got stuck, guys. Maybe there is simple solution to my problem. Please, help me. Here is the issue.

I have 5 possible options to choose from. Let's say [a, b, c, d, e]

My layout is two buttons and select dropdown menu. In other words, I give two possibilities to user:

Either click on one of two buttons or select from list.

So template is kinda

<button> A <button>
<button> B <button>
<select v-model="selected">
  <option :value="i" v-for="i in list">i</option>
</select> 

In my script I have

data () {
      return {
         list: ['c', 'd', 'e']
      }
    }

How can I combine these three elements into one. Can I somehow use v-model? Cause of now, I can get selected option from list. What should I do then. If I add @click event to buttons, what should they handle?

I thought to make computed value, like checking if selected is not undefined or equal to '', then the option is selected. But in order to bind buttons clicked events, I should create methods to handle their state, like

data () {
      return {
         list: ['c', 'd', 'e'],
         a: false,
         b: false
      }
    },
methods: {
  handleA () {
    this.a = true
    this.b = false     
  },
  handleB () {
    this.b = true
    this.a = false     
  }
}

And then every click will change false to true, and besides there also needs to be a logic to keep only one true state: either for a (button A), b (button B) or selected. It feels too wrong to be true. In my opinion, I'm making things harder. There has to be an easy way.

Upvotes: 1

Views: 7575

Answers (2)

Roy J
Roy J

Reputation: 43899

The basic problem is that if you v-model in a select and the variable bound to the v-model gets a value that is not in your options, the variable gets reset to null.

You have to come up with some special-case handling to get around that. One way is to set the select list to v-model a settable computed that interprets non-listed values as null and writes non-null values to the selected data item.

new Vue({
  el: '#app',
  data: {
    selected: null,
    list: ['c', 'd', 'e']
  },
  computed: {
    proxySelected: {
      get() {
        return this.list.includes(this.selected) ? this.selected : null;
      },
      set(newValue) {
        if (newValue) {
          this.selected = newValue;
        }
      }
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
  <button @click="selected = 'a'"> A </button>
  <button @click="selected = 'b'"> B </button>
  <select v-model="proxySelected">
    <option :value="i" v-for="i in list">{{i}}</option>
  </select>
  Selected value: {{selected}}
</div>

Upvotes: 1

Bert
Bert

Reputation: 82469

I'd recommend a component. Build the component to support v-model.

Vue.component("picker",{
  props:["value"],
  data(){
    return {
      list:["c","d","e"],
      currentValue: this.value,
      selectedValue: ""
    }
  },
  template:`
    <div>
      <button @click="currentValue = 'a'">A</button>
      <button @click="currentValue = 'b'">B</button>
      <select @change="currentValue = $event.target.value" v-model="selectedValue">
          <option value="">Select a value</option>
          <option v-for="item in list" :value="item" :key="item">{{item}}</option>
      </select>
    </div>
  `,
  watch:{
    currentValue(newValue){
      if (!this.list.includes(newValue))
        this.selectedValue = "" 

      this.$emit('input', newValue)
    }
  }
})

new Vue({
  el:"#app",
  data:{
    pickedValue: null
  }
})

Example.

Upvotes: 1

Related Questions