How to get index data from object used in v-for in select - vue.js

I have a component involving a select element. Below, opts is an array of objects.
Vue.component('atcf-select', {
props: [
'opts',
],
data() {
return {
element_index: '',
};
},
template: `
<div>
<select #change="onChange(opt,index)">
<option v-for="(opt,index) in opts">
{{ opt.text }} {{opt.index}}
</option>
</select>
</div>
`,
methods: {
onChange(opt,index) {
//Do something with opt and index...
}
}
};
The problem is obviously I cannot get the selected opt object and its index, and use it as a parameter for onChange method. What is the correct way to get the selected option's index and object?

You won't be able to pass the opt or index values to the change listener on the select element because it is outside the scope of the v-for.
If you don't specify any parameters for the onChange handler, Vue will implicitly pass an event object. From there, you can get the selectedIndex value via e.target.selectedIndex.
Here's an example:
new Vue({
el: '#app',
data() {
return {
opts: [
{ value: 'a', text: 'A' },
{ value: 'b', text: 'B' },
{ value: 'c', text: 'C' },
]
}
},
methods: {
onChange(e) {
let index = e.target.selectedIndex;
let option = this.opts[index];
console.log(index, option);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.8/vue.min.js"></script>
<div id="app">
<select #change="onChange">
<option v-for="(opt, index) in opts" :key="index" :value="opt.value">
{{ opt.text }}
</option>
</select>
</div>

You can use v-model
{{ option.value }} - {{ option.text }}
Index of {{valeureSelectionnee}} is : {{ IndexValeureSelectionnee }}

Related

vue js append params to URL

I am new to vuejs and I am making an app which should filters all the data from database and filter parameters should be passed in URL, so that it remain in the page even after page refresh.
Firstly, I want to push search params in URL
My current Url localhost:8080/product
I want my Url to be like below when user click checkboxes
localhost:8080/product?color=red&color=green&size=small (When user checks red, green and small options)
So far, I have done this and I am stuck how to get dynamic color in $this.router.push(), and append it to URL
<template>
<div class="products">
<h1>Filter By Color</h1>
<input #click="filterData" v-model="colortype" type="checkbox">Red color
<input #click="filterData" v-model="colortype" type="checkbox">Green color
<input #click="filterData" v-model="colortype" type="checkbox">Yellow color
<h1>Filter By Size</h1>
<input #click="filterData" v-model="size" type="radio">Big
<input #click="filterData" v-model="size" type="radio">Small
</div>
</template>
<script>
export default {
data() {
return {
colortype:''
}
},
methods:{
filterData() {
this.$router.push({ path: "product", query: { color: "red" } });
}
}
}
</script>
Any suggestions, once I push params to URL, I want to do api request to endpoint in filterData method.
There is a pattern you can use for this:
Store the filter values on an object
Deeply watch the filter object and react to it by fetching data / updating url.
The following snippets demonstrate a dropdown where you can pick one value to filter on.
<template>
<div>
Color:
<select v-model="filter.color">
<option v-for="item in colors" :key="item.value" :value="item.value">
{{ item.name }}
</option>
</select>
</div>
</template>
export default {
name: "MyComponent",
data: () => ({
filter: {
color: null,
},
colors: [{name: 'Black', value: 'black'}, {name: 'Red', value: 'red'}],
}),
watch: {
'filter': {
deep: true,
handler(filter) {
this.$router.replace({
...this.$route,
query: {
color: filter.color.value,
// TODO: Convert `filter` to params
},
});
this.search(filter);
},
},
},
};
For the case where there a field accepts multiple values (e.g. using checkboxes) I'd suggest to put all values on the filter object and have each option mark its checked-ness with a flag. The following snippets demonstrate that technique:
<template>
<div>
Sizes:
<label v-for="size in sizes" :key="size.value">
<input type="checkbox" v-model="size.active">
{{ size.name }}
</label>
</div>
</template>
export default {
data: () => ({
filter: {
sizes: [{name: 'Small', value: 'small', active: false}],
},
}),
watch: {
filter: {
deep: true,
handler() {
this.$router.replace({
...this.$route,
params: {
sizes: this.filter.sizes
.filter(size => size.active)
.map(size => size.value)
.join('+'),
},
});
},
},
},
}

Get each HTML element in a Vue slot from JavaScript

I am creating a custom select component in VueJS 2. The component is to be used as below by the end-user.
<custom-select>
<option value="value 1">Option 1</option>
<option value="value 2">Option 2</option>
<option value="value 3">Option 3</option>
...
<custom-select>
I know the Vue <slot> tag and usage. But how do I get the user provided <option> tags as an array/list so I can get its value and text separately for custom rendering inside the component?
Those <option>s would be found in the default slot array (this.$slots.default), and you could get to the inner text and value of the <option>s like this:
export default {
mounted() {
const options = this.$slots.default.filter(node => node.tag === 'option')
for (const opt of options) {
const innerText = opt.children.map(c => c.text).join()
const value = opt.data.attrs.value
console.log({ innerText, value })
}
}
}
demo
You can achieve it, using v-bind and computed property
new Vue({
el: '#vue',
data: {
selected: '',
values: [
{
code: '1',
name: 'one'
},
{
code: '2',
name: 'two'
}
]
},
computed: {
selectedValue() {
var self = this;
var name = "";
this.values.filter(function(value) {
if(value.code == self.selected) {
name = value.name
return;
}
})
return name;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vue">
<div>
<select v-model="selected">
<option v-for="value in values" v-bind:value="value.code">
{{ value.name }}
</option>
</select>
</div>
<strong>{{ selected }} {{ selectedValue }}</strong>
</div>

Vue.js custom component with HTML <select> and v-model (W3C compliant)

I'm new to Vue.js (using Nuxt.js) and what I'm trying to achieve is to have a Select component that I can reuse everywhere and is W3C compliant.
With the help of #Jasmonate answers, I managed to create this component, it's working. But the value attribute is still visible in the source code and so isn't W3C compliant. Maybe the problem is coming from somewhere else in the project ?!
Parent component
<custom-select
:options="options"
v-model="selectedOption"
></custom-select>
<span>Selected : {{ selectedOption }}</span>
<script>
data() {
return {
selectedOption: "A",
options: [
{ label: "One", value: "A" },
{ label: "Two", value: "B" },
{ label: "Three", value: "C" }
],
};
}
</script>
custom-select.vue
<template>
<select :value="value" #input="clicked">
<option
v-for="option in options"
:key="option.label"
:value="option.value"
>
{{ option.label }}
</option>
</select>
</template>
<script>
export default {
props: {
value: {
required: true
},
options: {
type: Array,
required: true
}
},
methods: {
clicked($event) {
this.$emit("input", $event.target.value);
}
}
};
</script>
I read those documentation pages:
Form Input Bindings
Components Basics
And also looked around the web to find example of v-model in a custom component, but it's always about the input tag. The only example I found about a custom select with v-model isn't actually a select tag, like the Vue Select plugin or this thread on StackOverflow.
v-model is syntax sugar. By default, the value is a prop that has the name value, and it changes (two-way-binding) whenever the event input is emitted.
Also, v-model is bound on the select element, not option.
Your code can be modified as such:
<template>
<select :value="value" #input="clicked">
<option
v-for="option in options"
:key="option.label"
:value="option.value"
>
{{ option.label }}
</option>
</select>
</template>
<script>
export default {
props: {
value: {
required: true
},
options: {
type: Array,
required: true
}
},
methods: {
clicked($event) {
this.$emit('input', $event.target.value);
}
}
};
</script>
Documentation here: https://v2.vuejs.org/v2/guide/components.html#Using-v-model-on-Components
You can also change the prop name and event name that v-model uses, see: https://v2.vuejs.org/v2/guide/components-custom-events.html#Customizing-Component-v-model

select value unset after click button

select with options are generated dynamically.
When button 'GO' were click select options generates again with different data. But the issue is when user select option 1 and click 'go' automatically option 1 is selected from next select, how to clear that?
my code:
<select v-model="key" #change="onChange($event)">
<option v-for="(option, index) in passSelect" :value="index" v-if="option!==null" :data-foo="option" #click="onChange($event)" #click="onClick($value)">
{{ option }}
</option>
</select>
<button #click="onChildClick" class="button">Go -></button>
methods:
onChildClick() {
this.counter++;
this.loadAgain();
},
getSelect(){
this.passSelect = this.checkExist();
},
onChange(event) {
this.selectedIndex = event.target.value;
this.selectedValue = event.target.options[event.target.value].dataset.foo;
},
loadAgain(){
this.getSelect();
},
Selection is determined by the v-model, which is using the property key. As you're using the index for the value this will cause the option with the same index as the previous list to be selected.
You need to clear the value of key to reset the <select>:
new Vue({
el: '#app',
data () {
return {
counter: 0,
key: null,
passSelect: [],
selectedIndex: null,
selectedValue: null
}
},
created () {
this.getSelect();
},
methods: {
onChildClick () {
this.counter++;
this.loadAgain();
},
getSelect () {
this.key = null;
this.selectedIndex = null;
this.selectedValue = null;
this.passSelect = getOptionsList();
},
onChange (event) {
const value = event.target.value;
if (value) {
this.selectedIndex = event.target.value;
this.selectedValue = event.target.options[event.target.value].dataset.foo;
}
},
loadAgain () {
this.getSelect();
}
}
})
function getOptionsList () {
return ['Red', 'Yellow', 'Green', 'Brown', 'Blue', 'Pink', 'Black'].filter(c => {
return Math.random() > 0.4;
});
}
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<select v-model="key" #change="onChange($event)">
<option v-for="(option, index) in passSelect" :value="index" v-if="option !== null" :data-foo="option">
{{ option }}
</option>
</select>
<button #click="onChildClick" class="button">Go -></button>
<p>counter: {{ JSON.stringify(counter) }}</p>
<p>key: {{ JSON.stringify(key) }}</p>
<p>selectedIndex: {{ JSON.stringify(selectedIndex) }}</p>
<p>selectedValue: {{ JSON.stringify(selectedValue) }}</p>
</div>
I've also cleared selectedIndex and selectedValue to try to keep the data consistent.
Some other notes:
I've got rid of the 2 click listeners on the <option>. Not really sure what they were for but you shouldn't have had 2 listeners for the same event on the same element.
key and selectedIndex are almost the same thing. The only difference is that selectedIndex ends up being a string whereas key is a number. Not clear why you aren't just using selectedIndex for your v-model directly.
I don't know what checkExist does but passSelect feels like it should be a computed property from the code provided.
There shouldn't be any need to use data-foo to pass the option to the listener. You can get the relevant option directly from the data once you have the index. All of which assumes you actually need the index, otherwise you could just bind the value directly to the string option.

Vue.js - Watcher for property with variable keypath

Is it possible, in Vue v2, to define a watcher using a keypath containing a variable?
For example, depending on the currentKey, I want to watch either the changes in obj.A or obj.B:
data() {
return {
currentKey: 'A',
obj: { A: { 'a': '' }, B: { 'b' :'' },
}
},
watch: {
'obj[currentKey]'(newItem, oldItem) {}
}
You can make a computed property which returns this.obj[this.currentKey] and then set a watcher on that.
But, if you want to watch changes to the properties of the dynamic object, you'll also need to set the deep property of the watcher to true.
Here's a simple example:
new Vue({
el: '#app',
data () {
return {
currentKey: 'A',
obj: {A: {value: ''}, B: {value:''} },
}
},
computed: {
selected() {
return this.obj[this.currentKey];
}
},
watch: {
selected: {
deep: true,
handler(object) {
console.log('selected object value', object.value);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.9/vue.min.js"></script>
<div id="app">
Selected object: {{ selected }}
<select v-model="currentKey">
<option v-for="i in ['A', 'B']" :key="i" :value="i">{{ i }}</option>
</select>
<br><br>
Text for selected object: <input v-model="selected.value">
</div>