Search code examples
javascriptvue.jsvuejs2v-forv-model

How do I get values from multiple vue select instances?


I have 2 arrays of objects like below. I need to create a select for every questions element, and I need to have the options connected with the selections by the id. In this case I need to have 2 selects, the first one will have 1000, 5000 and 10000 as options meanwhile the second select will have yes and no as options

const questions = [{
        'id': 1,
        'question': 'KM'
    },
    {
        'id': 2,
        'question': 'Works'
    }
]

const selections = [{
        'question_id': 1,
        'value': 1000
    },
    {
        'question_id': 1,
        'value': 5000
    },
    {
        'question_id': 1,
        'value': 10000
    },
    {
        'question_id': 2,
        'value': 'yes'
    },
    {
        'question_id': 2,
        'value': 'no'
    }
]

I made it like this in vue but the issue is that I can't save the data in the v-model as I'm returning the values from cars() and not a variable specified in data()

<div class="form-group" v-for="item in questions" v-bind:key="item.question">
    <h5 v-if="showQuestion(item.id)">{{ item.question }}</h5>

    <div class="tour-options-select" v-if="showQuestion(item.id)">
        <select id="select-suggestions" name="tour-options-dropdown" class="tour-options-dropdown" v-model="questions.value">
            <option v-for="item1 in cars(item.id)" v-bind:key="item1.id" :value="item1.display_name">{{ item1.display_name }}</option>
        </select>
    </div>
</div>

Ultimately, I just need to know, how do I get the value when I have a structure like the one I defined above?


Solution

  • If you have an array in data() to store your selected options, you can use v-model to dynamically bind with an element in that array if you give it an index:

    new Vue({
      el: '#app',
      data: {
        questions: [{
            'id': 1,
            'question': 'KM'
          },
          {
            'id': 2,
            'question': 'Works'
          }
        ],
        selections: [{
            'question_id': 1,
            'value': 1000
          },
          {
            'question_id': 1,
            'value': 5000
          },
          {
            'question_id': 1,
            'value': 10000
          },
          {
            'question_id': 2,
            'value': 'yes'
          },
          {
            'question_id': 2,
            'value': 'no'
          }
        ],
        selected: [],
      },
      methods: {
        cars: function(id) {
          return this.selections.reduce((arr, currSel) => {
            if (currSel.question_id == id)
              arr.push(currSel);
            return arr;
          }, []);
        },
      }
    });
    <script src="https://unpkg.com/vue@2.6.12/dist/vue.min.js"></script>
    <div id="app">
      <div v-for="(question, index) in questions" :name="question.question" :key="question.id">
        <select v-model="selected[index]">
          <option v-for="option in cars(question.id)" :key="option.question_id" :value="option.value">
            {{ option.value }}
          </option>
        </select>
      </div>
    
      <p>Selected:</p>
      <pre>{{ $data.selected }}</pre>
    </div>

    Another approach would be to use events to handle the changes, calling a custom function each time the user makes a selection, eg using @change:

    new Vue({
      el: '#app',
      data: {
        questions: [{
            'id': 1,
            'question': 'KM'
          },
          {
            'id': 2,
            'question': 'Works'
          }
        ],
        selections: [{
            'question_id': 1,
            'value': 1000
          },
          {
            'question_id': 1,
            'value': 5000
          },
          {
            'question_id': 1,
            'value': 10000
          },
          {
            'question_id': 2,
            'value': 'yes'
          },
          {
            'question_id': 2,
            'value': 'no'
          }
        ],
      },
      methods: {
        cars: function(id) {
          return this.selections.reduce((arr, currSel) => {
            if (currSel.question_id == id)
              arr.push(currSel);
            return arr;
          }, []);
        },
      }
    });
    <script src="https://unpkg.com/vue@2.6.12/dist/vue.min.js"></script>
    <div id="app">
      <div v-for="(question, index) in questions" :name="question.question" :key="question.id">
        <select @change="console.log('Option', $event.target.value, 'chosen for Q', question.id)">
          <option selected disabled>Select...</option>
          <option v-for="option in cars(question.id)" :key="option.question_id" :value="option.value">
            {{ option.value }}
          </option>
        </select>
      </div>
    </div>

    This way will give you more freedom to store or process the data as you wish, but you'll have to do it manually.