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.
Related
How are you?
I'm studying Vue and I'm stuck on the current task not knowing where to go.
I have a select that when I click I need to show on screen only what corresponds to that selection. For example, when placing the "to do" option in the select, only the tasks with a concluded=false should appear on the screen. I've only gotten this far and I need help to continue. Can you help me? Thanks
This is my App.vue
<template>
<div id="app">
<h1>Lista de Tarefas</h1>
<List :data="list" #remove="handleRemove"/>
<Form #add="addNewTask" #onChange="handleN"/>
</div>
</template>
<script>
import List from "./components/List.vue";
import Form from "./components/Form.vue";
export default {
components: {
List,
Form,
},
data() {
return {
list: [],
};
},
methods: {
addNewTask(newTask) {
this.list.push(newTask);
},
handleRemove(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].excluded = true
},
handleN(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].concluded = true
}
},
};
</script>
This is my List.vue
<template>
<ul>
<select v-model="selected" #change="onChange($event)">
<option disabled value="">Escolha a visualização</option>
<option v-for="option in options" :key="option.text">
{{ option.text }}
</option>
</select>
<li v-for="item in itens" :key="item.id">
<input type="checkbox" id="checkbox" v-model="item.concluded" />
<label for="checkbox"> {{ item.description }} </label>
<button #click="() => $emit('remove', item)">Excluir</button>
</li>
</ul>
</template>
<script>
export default {
props: {
data: {
type: Array,
default: () => {},
},
},
data() {
return {
selected: "",
options: [
{ text: "Todos", value: "1" },
{ text: "A fazer", value: "2" },
{ text: "Concluído", value: "3" },
{ text: "Deletado", value: "4" },
],
};
},
computed: {
itens() {
return this.data.filter((item) => item.excluded === false);
},
},
methods: {
onChange(event) {
console.log(event.target.value);
return this.data.filter((item) => item.concluded === false);
},
},
};
</script>
This is my Form.vue
<template>
<form #submit.prevent="handleNewTask">
<input type="text" v-model="newTask" placeholder="Insira a tarefa"/>
<input type="submit" value="Adicionar"/>
</form>
</template>
<script>
import Task from '../types/Task.js'
export default {
data() {
return {
newTask: "",
};
},
methods: {
handleNewTask() {
this.$emit('add', new Task(this.newTask))
this.newTask = ''
}
},
};
</script>
And this is my Task.js
export default class {
constructor(description) {
this.description = description,
this.id = Math.random(),
this.concluded = false,
this.excluded = false
}
}
I watch some tutorials, read the documentation and some StackOverflow questions but I really can't get out of here
Thanks in advance for the help
Based on how you have structured your app, our only concern should be with the List.vue file.
Your goal is to filter the results based on the selection (selected property). However, your issue is that you are not even using that anywhere.
I know you are hard coding the filter on the onChange method but that is, first of all wrong because you aren't really changing anything (you are returning an array), and secondly it's inefficient.
A better way to do it is to update the computed itens function like so:
itens() {
return this.data.filter((item) => {
if (this.selected === '1'){
return item.concluded === false
} else if (this.selected === '2'){
// filter another way
} else if (... // so on and so forth
});
},
Also, I would filter out the excluded items before sending them to the component. If you aren't going to use it, don't send it.
Remove the onChange event on the <select> and the associated method since they are now unused.
I need to set "message" on page load and later update it by select box from array in data. But by page load, the item is undefined - what make I wrong? Thank you for advice.
the code is as well on: https://repl.it/#DaBor/find#index.html
let vueApp = new Vue({
el: '#app',
data: {
selected: '1',
options: [
{id:1, projectNAME:"bobx", note:"note-bobx"},
{id:2, projectNAME:"danx", note:"note-danx"},
{id:3, projectNAME:"barb", note:"note-barb"},
],
message: "start"
},
methods: {
foo: function(){
var item = this.options.find(item => item.id === this.selected);
alert(item);
this.message = item.note;
},
},
mounted() {
this.foo();
},
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<select v-model="selected" v-on:change="foo" >
<option v-for="option in options" v-bind:value="option.id">
{{ option.projectNAME }}
</option>
</select>
<span>Selected id:{{ selected }}</span>
<br />
<span v-html="message"></span>
</div>
The find() callback compares item.id (a Number) to selected (a String):
var item = this.options.find(item => item.id === this.selected);
^ ^
Number String
Strict equality (===) compares both type and value. Since the two types are different in this case, the comparison would always be false, resulting in undefined being returned from find().
One solution is to convert selected to a Number, and use that to compare:
var selectedId = Number(this.selected);
var item = this.options.find(item => item.id === selectedId);
updated repl.it
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 }}
I have an object that contains user names in this structure:
clients: {
1: {
first_name:"John"
last_name:"Doe"
middle_name:"A"
},
2: {
first_name:"Jenny"
last_name:"Doe"
},
}
I want to loop though them in a select input as options
<option v-for="(value, client, index) in clients" :value="">{{ value }}</option>
I came until here, but I couldn't figure out how to organize the string properly. Maybe, is there a way to parse it in computed properties so I can have room to put code?
If I could use something like this, I think it would work, but couldn't figure out how to do it like this either.
computed:{
clientNameOptions() {
for (const key of Object.keys(this.clients)) {
return `<option value="` + this.clients[key].first_name + ' ' + this.clients[key].middle_name + ' ' +
this.clients[key].last_name + `"></option>`
}
}
}
What is the proper way of achieving it?
This is primarily a guess at what you want, but you could build your options using a computed property.
console.clear()
new Vue({
el:"#app",
data:{
clients: {
1: {
first_name:"John",
last_name:"Doe",
middle_name:"A"
},
2: {
first_name:"Jenny",
last_name:"Doe"
},
},
selectedClient: null
},
computed:{
options(){
return Object.keys(this.clients).map(k => {
let o = this.clients[k]
return `${o.first_name} ${o.middle_name ? o.middle_name : ''} ${o.last_name}`
})
}
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<select v-model="selectedClient">
<option v-for="option in options" :value="option">{{option}}</option>
</select>
<hr>
Selected: {{selectedClient}}
</div>
Simple question : is it possible to watch when an object as a new property ?
In the JSFiddle I only initialize the property "show" of the first item in the list".
When I click on "Toggle" for first item it works.
But for the others items (that get "show" property dynamically) and for myObject (that get "test2" property dynamically) it is refresh only when you toggle the first item of the list !
In my case I want that the toggle works immediately.
Someone has an explanation ?
new Vue({
el: "#app",
data: function() {
return {
myObject: { test: 'test' },
list: [{
value: 1,
show: true
}, {
value: 2
}, {
value: 3
}, {
value: 4
}, {
value: 5
}]
}
},
methods: {
toggleItem: function( item ) {
if( !item.show ) item.show = true;
else item.show = !item.show;
console.log(item.show)
this.myObject.test2 = 'test2';
}
},
watch: {
list: function( newValue ) {
console.log('NEW VALUE: ', newValue );
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.5/vue.js"></script>
<div id="app">
{{ myObject }}
<div v-for="item in list">
<span v-if="item.show">
{{ item.value }}
</span>
<button #click="toggleItem(item)" type="button">Toggle !</button>
</div>
</div>
To help Vue pick up new properties, you have to use Vue.set() / this.$set to add them. Otherwise, Vue can't detect the addition and the new property will not be reactive.
if( !item.show ) this.$set(item, 'show', true)
https://v2.vuejs.org/v2/api/#Vue-set
Generally, We strongly advise to set all properties beforehand in data()