Skip to content
Fix Code Error

Vue.js custom select component with v-model

March 22, 2021 by Code Error
Posted By: Anonymous

I want to create a custom select component in Vue.js. Since I need specific options styling, I need to create ‘select’ made of div’s etc that looks and acts like a real html select.

Currently I have something like this:

_x000D_

_x000D_

Vue.component('child', {_x000D_
  template: `<div class="component-container"  @click="showOptions = !showOptions">_x000D_
        <div class="component__select">_x000D_
            <span class="component__select--name">Select Fruit</span>_x000D_
            _x000D_
            <span class="c-arrow-down" v-if="!showOptions"></span>_x000D_
            <span class="c-arrow-up" v-if="showOptions"></span>_x000D_
        </div>_x000D_
        <ul class="component__select-options" v-if="showOptions" >_x000D_
            <li class="select--option" v-for="option in options">_x000D_
                <label> <input type="checkbox" :value="option"/> {{option.name}}</label>_x000D_
            </li>_x000D_
        </ul>_x000D_
    </div>`,_x000D_
_x000D_
  methods: {_x000D_
    selectOption(option) {_x000D_
      this.$emit('option', option)_x000D_
    }_x000D_
  },_x000D_
  data: () => ({_x000D_
  showOptions: false,_x000D_
  }),_x000D_
  props: ['options']_x000D_
});_x000D_
_x000D_
var vm = new Vue({_x000D_
  el: '#app',_x000D_
  data: () => ({_x000D_
	options: [_x000D_
  {id: 0, name: 'Apple'},_x000D_
  {id: 1, name: 'Banana'},_x000D_
  {id: 2, name: 'Orange'},_x000D_
  {id: 2, name: 'Strawberry'},_x000D_
  ],_x000D_
  selectedFruit: ''_x000D_
  }),_x000D_
})

_x000D_

 .component__select {_x000D_
   height: 38px;_x000D_
   background-color: #F5F7FA;_x000D_
   border: 1px solid #dddddd;_x000D_
   line-height: 38px;_x000D_
   display: grid;_x000D_
   max-width: 500px;_x000D_
   grid-template-columns: 10fr 1fr;_x000D_
 }_x000D_
_x000D_
 .component__select--name {_x000D_
   font-size: 0.8rem;_x000D_
   padding: 0 0 0 25px;_x000D_
   cursor: pointer;_x000D_
 }_x000D_
_x000D_
 .c-arrow-down {_x000D_
   justify-self: end;_x000D_
 }_x000D_
_x000D_
 .component__select-options {_x000D_
   max-height: 180px;_x000D_
   border: 1px solid #dddddd;_x000D_
   border-top: none;_x000D_
   overflow: auto;_x000D_
   position: absolute;_x000D_
   z-index: 1500;_x000D_
   max-width: 500px;_x000D_
   width: 500px;_x000D_
   margin: 0;_x000D_
   padding: 0;_x000D_
 }_x000D_
_x000D_
 .select--option {_x000D_
   height: 35px;_x000D_
   display: grid;_x000D_
   align-content: center;_x000D_
   padding: 0 0 0 25px;_x000D_
   background-color: #f5f5fa;_x000D_
   border-bottom: 1px solid #dddddd;_x000D_
 }_x000D_
_x000D_
 .select--option:last-child {_x000D_
   border-bottom: none;_x000D_
 }_x000D_
_x000D_
 .select--option:nth-child(2n) {_x000D_
   background-color: #ffffff;_x000D_
 }_x000D_
 _x000D_
 .select--option input{_x000D_
   display: none;_x000D_
 }_x000D_
_x000D_
 .single-option {_x000D_
   height: 55px;_x000D_
   background-color: #2595ec;_x000D_
   font-size: 0.8rem;_x000D_
   border: 1px solid red;_x000D_
 }_x000D_
_x000D_
 .cust-sel {_x000D_
   width: 200px;_x000D_
   height: 38px;_x000D_
   background-color: #f5f5fa;_x000D_
   border: 1px solid #dddddd;_x000D_
 }_x000D_
_x000D_
 .cust-sel:focus {_x000D_
   outline-width: 0;_x000D_
 }

_x000D_

<html>_x000D_
<head>_x000D_
  <title>An example</title>_x000D_
</head>_x000D_
<body>_x000D_
  <div id="app">_x000D_
    <span> This is parent component</span>_x000D_
    <p>I want to have data from select here:  "{{selectedFruit}}"</p>_x000D_
    <child :options="options" v-model="selectedFruit"></child>_x000D_
  </div>_x000D_
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>_x000D_
</body>_x000D_
</html>

_x000D_

_x000D_

_x000D_

But my problem is now how to return data from child to parent component using v-model on child component.

(I know I could emit data from child component and do:
<custom-select :options="someOptions" @selected="setSelectedOption"/>
but I need it to be reusable and writing more and more methods to retrieve data from every select in parent component is not exactly how it should work I think.)

Also I need to have an entire object returned, not only ID. (that’s why i’ve got :value="option")
Any ideas?

Solution

As Vue Guide said:

v-model is essentially syntax sugar for updating data on user input
events, plus special care for some edge cases.

The syntax sugar will be like:

the directive=v-model will bind value, then listen input event to make change like v-bind:value="val" v-on:input="val = $event.target.value"

So for your use case, you need to create one prop=value, then emit the selected option with event=input.

Like below demo (bind/emit the whole option object):

_x000D_

_x000D_

Vue.config.productionTip = false_x000D_
Vue.component('child', {_x000D_
  template: `<div class="component-container"  @click="showOptions = !showOptions">_x000D_
        <div class="component__select">_x000D_
            <span class="component__select--name">{{value ? value.name : 'Select Fruit'}}</span>_x000D_
            _x000D_
            <span class="c-arrow-down" v-if="!showOptions"></span>_x000D_
            <span class="c-arrow-up" v-if="showOptions"></span>_x000D_
        </div>_x000D_
        <ul class="component__select-options" v-if="showOptions" >_x000D_
            <li class="select--option" v-for="option in options" @click="selectOption(option)">_x000D_
                <label> <input type="checkbox" :value="option"/> {{option.name}}</label>_x000D_
            </li>_x000D_
        </ul>_x000D_
    </div>`,_x000D_
_x000D_
  methods: {_x000D_
    selectOption(option) {_x000D_
      this.$emit('input', option)_x000D_
    }_x000D_
  },_x000D_
  data: () => ({_x000D_
  	showOptions: false_x000D_
  }),_x000D_
  props: ['options', 'value']_x000D_
});_x000D_
_x000D_
var vm = new Vue({_x000D_
  el: '#app',_x000D_
  data: () => ({_x000D_
	options: [_x000D_
  {id: 0, name: 'Apple'},_x000D_
  {id: 1, name: 'Banana'},_x000D_
  {id: 2, name: 'Orange'},_x000D_
  {id: 2, name: 'Strawberry'},_x000D_
  ],_x000D_
  selectedFruit: ''_x000D_
  }),_x000D_
})

_x000D_

 .component__select {_x000D_
   height: 38px;_x000D_
   background-color: #F5F7FA;_x000D_
   border: 1px solid #dddddd;_x000D_
   line-height: 38px;_x000D_
   display: grid;_x000D_
   max-width: 500px;_x000D_
   grid-template-columns: 10fr 1fr;_x000D_
 }_x000D_
_x000D_
 .component__select--name {_x000D_
   font-size: 0.8rem;_x000D_
   padding: 0 0 0 25px;_x000D_
   cursor: pointer;_x000D_
 }_x000D_
_x000D_
 .c-arrow-down {_x000D_
   justify-self: end;_x000D_
 }_x000D_
_x000D_
 .component__select-options {_x000D_
   max-height: 180px;_x000D_
   border: 1px solid #dddddd;_x000D_
   border-top: none;_x000D_
   overflow: auto;_x000D_
   position: absolute;_x000D_
   z-index: 1500;_x000D_
   max-width: 500px;_x000D_
   width: 500px;_x000D_
   margin: 0;_x000D_
   padding: 0;_x000D_
 }_x000D_
_x000D_
 .select--option {_x000D_
   height: 35px;_x000D_
   display: grid;_x000D_
   align-content: center;_x000D_
   padding: 0 0 0 25px;_x000D_
   background-color: #f5f5fa;_x000D_
   border-bottom: 1px solid #dddddd;_x000D_
 }_x000D_
_x000D_
 .select--option:last-child {_x000D_
   border-bottom: none;_x000D_
 }_x000D_
_x000D_
 .select--option:nth-child(2n) {_x000D_
   background-color: #ffffff;_x000D_
 }_x000D_
 _x000D_
 .select--option input{_x000D_
   display: none;_x000D_
 }_x000D_
_x000D_
 .single-option {_x000D_
   height: 55px;_x000D_
   background-color: #2595ec;_x000D_
   font-size: 0.8rem;_x000D_
   border: 1px solid red;_x000D_
 }_x000D_
_x000D_
 .cust-sel {_x000D_
   width: 200px;_x000D_
   height: 38px;_x000D_
   background-color: #f5f5fa;_x000D_
   border: 1px solid #dddddd;_x000D_
 }_x000D_
_x000D_
 .cust-sel:focus {_x000D_
   outline-width: 0;_x000D_
 }

_x000D_

<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>_x000D_
<div id="app">_x000D_
  <span> This is parent component</span>_x000D_
  <p>I want to have data from select here:  "{{selectedFruit}}"</p>_x000D_
  <child :options="options" v-model="selectedFruit"></child>_x000D_
</div>

_x000D_

_x000D_

_x000D_

Answered By: Anonymous

Related Articles

  • Is CSS Turing complete?
  • Form field border-radius is not working only on the last…
  • Customize Bootstrap checkboxes
  • How can I hide a div in all instances of a component while I…
  • Why the value of input file missing when I input the another…
  • How do I limit the number of digits from 6 to 4 in JS?…
  • How to sort an ArrayList in Java
  • VueJS: Cannot read property 'name' of undefined"
  • Error: the entity type requires a primary key
  • Python: Tuples/dictionaries as keys, select, sort

Disclaimer: This content is shared under creative common license cc-by-sa 3.0. It is generated from StackExchange Website Network.

Post navigation

Previous Post:

List of icons and their names in iron-icons

Next Post:

Can I use jquery within my custom Polymer elements?

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

  • Get code errors & solutions at akashmittal.com
© 2022 Fix Code Error