Sunday, May 19, 2024
 Popular · Latest · Hot · Upcoming
57
rated 0 times [  64] [ 7]  / answers: 1 / hits: 17214  / 6 Years ago, wed, june 20, 2018, 12:00:00

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:





Vue.component('child', {
template: `<div class=component-container @click=showOptions = !showOptions>
<div class=component__select>
<span class=component__select--name>Select Fruit</span>

<span class=c-arrow-down v-if=!showOptions></span>
<span class=c-arrow-up v-if=showOptions></span>
</div>
<ul class=component__select-options v-if=showOptions >
<li class=select--option v-for=option in options>
<label> <input type=checkbox :value=option/> {{option.name}}</label>
</li>
</ul>
</div>`,

methods: {
selectOption(option) {
this.$emit('option', option)
}
},
data: () => ({
showOptions: false,
}),
props: ['options']
});

var vm = new Vue({
el: '#app',
data: () => ({
options: [
{id: 0, name: 'Apple'},
{id: 1, name: 'Banana'},
{id: 2, name: 'Orange'},
{id: 2, name: 'Strawberry'},
],
selectedFruit: ''
}),
})

 .component__select {
height: 38px;
background-color: #F5F7FA;
border: 1px solid #dddddd;
line-height: 38px;
display: grid;
max-width: 500px;
grid-template-columns: 10fr 1fr;
}

.component__select--name {
font-size: 0.8rem;
padding: 0 0 0 25px;
cursor: pointer;
}

.c-arrow-down {
justify-self: end;
}

.component__select-options {
max-height: 180px;
border: 1px solid #dddddd;
border-top: none;
overflow: auto;
position: absolute;
z-index: 1500;
max-width: 500px;
width: 500px;
margin: 0;
padding: 0;
}

.select--option {
height: 35px;
display: grid;
align-content: center;
padding: 0 0 0 25px;
background-color: #f5f5fa;
border-bottom: 1px solid #dddddd;
}

.select--option:last-child {
border-bottom: none;
}

.select--option:nth-child(2n) {
background-color: #ffffff;
}

.select--option input{
display: none;
}

.single-option {
height: 55px;
background-color: #2595ec;
font-size: 0.8rem;
border: 1px solid red;
}

.cust-sel {
width: 200px;
height: 38px;
background-color: #f5f5fa;
border: 1px solid #dddddd;
}

.cust-sel:focus {
outline-width: 0;
}

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





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?


More From » vue.js

 Answers
297

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):




Vue.config.productionTip = false
Vue.component('child', {
template: `<div class=component-container @click=showOptions = !showOptions>
<div class=component__select>
<span class=component__select--name>{{value ? value.name : 'Select Fruit'}}</span>

<span class=c-arrow-down v-if=!showOptions></span>
<span class=c-arrow-up v-if=showOptions></span>
</div>
<ul class=component__select-options v-if=showOptions >
<li class=select--option v-for=option in options @click=selectOption(option)>
<label> <input type=checkbox :value=option/> {{option.name}}</label>
</li>
</ul>
</div>`,

methods: {
selectOption(option) {
this.$emit('input', option)
}
},
data: () => ({
showOptions: false
}),
props: ['options', 'value']
});

var vm = new Vue({
el: '#app',
data: () => ({
options: [
{id: 0, name: 'Apple'},
{id: 1, name: 'Banana'},
{id: 2, name: 'Orange'},
{id: 2, name: 'Strawberry'},
],
selectedFruit: ''
}),
})

 .component__select {
height: 38px;
background-color: #F5F7FA;
border: 1px solid #dddddd;
line-height: 38px;
display: grid;
max-width: 500px;
grid-template-columns: 10fr 1fr;
}

.component__select--name {
font-size: 0.8rem;
padding: 0 0 0 25px;
cursor: pointer;
}

.c-arrow-down {
justify-self: end;
}

.component__select-options {
max-height: 180px;
border: 1px solid #dddddd;
border-top: none;
overflow: auto;
position: absolute;
z-index: 1500;
max-width: 500px;
width: 500px;
margin: 0;
padding: 0;
}

.select--option {
height: 35px;
display: grid;
align-content: center;
padding: 0 0 0 25px;
background-color: #f5f5fa;
border-bottom: 1px solid #dddddd;
}

.select--option:last-child {
border-bottom: none;
}

.select--option:nth-child(2n) {
background-color: #ffffff;
}

.select--option input{
display: none;
}

.single-option {
height: 55px;
background-color: #2595ec;
font-size: 0.8rem;
border: 1px solid red;
}

.cust-sel {
width: 200px;
height: 38px;
background-color: #f5f5fa;
border: 1px solid #dddddd;
}

.cust-sel:focus {
outline-width: 0;
}

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




[#54159] Sunday, June 17, 2018, 6 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
yosefleod

Total Points: 113
Total Questions: 100
Total Answers: 115

Location: Egypt
Member since Tue, May 3, 2022
2 Years ago
;