Drop down list with Vue JS - javascript

I code an html page with a drop-down list with HTML CSS and VueJS. I want that if I click on a certain option in my drop-down list, it display something in console.
My html code :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="css/style.css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript" src="js/historique.js"></script>
<title>Historique</title>
</head>
<body>
<select id="selectionMachines" size="1">
<option v-for="todo in todos" v-on:click="action">
{{ todo.libelle }}
</option>
</select>
</body>
</html>
And my JS code :
window.onload = function () {
var machines = new Vue({
el:"#selectionMachines",
data: {
todos: [
{ libelle: "Machine 195", num: 195},
{ libelle: "Machine 196", num: 195}
]
},
methods: {
action: function() {
console.log(this.todo.num);
if (this.todos.num == 195)
console.log("J'ai choisi 195");
}
}
})
}
The action function doesn't work for the moment, undefined is log in console. Any idea ?

The event should be bound to <select>.
The event should pass the $event variable to the function
The options' values should be bound to the options themselves (:value=""). You'll get this value with the $event ($event.target.value)
Even though you literally click on a <select> and the <option>, the event is not click(), but input or change, as it's an input field. This is not VueJS question, but JavaScript.
Your action() method is a function, so if you want it to get executed, then you have to call it with parantheses (action()), and not it's "string value" (action). This is unlike computed in VueJS templates, but VueJS handles the difference.
Pay attention to your todos - both have a num key with value 195 (so you won't see a difference in the console, both will log 195 there), although their label (libelle) implies 195 and 196
You write console.log(this.todo.num); and this.todos.num == 195 in your action() method. I guess you tried to use the single todo item that's selected in the input. The problem with this is that the input (and the todo in todos) only exist in your template, your scripts (like methods) don't know about them - you have to pass the information somehow to those scripts ($event is good for that). If you want to get the whole single todo object to work with, then I suggest find() (or filter() as your num value is not unique).
new Vue({
el: "#selectionMachines",
data: {
todos: [{
libelle: "Machine 195",
num: 195
},
{
libelle: "Machine 196",
num: 195
}
]
},
methods: {
action: function(event) {
console.log(event.target.value)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<select id="selectionMachines" size="1" #change="action($event)">
<option v-for="todo in todos" :value="todo.num">
{{ todo.libelle }}
</option>
</select>

Try this:
<option v-for="todo in todos" #click="action(todo)">
{{ todo.libelle }}
</option>
action(todo) { if (todo.num === 195) console.log("J'ai choisi 195"); }

Related

Passing separate data through a select box change event, Vue

I have a working select box that is sending the selected value to a method on the change event but I have a question with this:
Say I want to send the cat_id value at the time of selection as well (so that I could build an object that relates the cat_id and date within the called method) is there a way to send that cat_id, or any other data, along in that select box change event?
var vm =
new Vue({
el: "#app",
props: {
},
data: {
testing_dates:['2021-11-29', '2021-11-30'],
cat_id: [123]
},
methods: {
testChange(event){
console.log(event.target.value);
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<li>Category ID: {{ cat_id }}</li>
<li style="list-style: none;">
<select style="width: auto;" #change="testChange($event)">
<option selected disabled>Options</option>
<option v-for="date in testing_dates" :value="date">{{ date }}</option>
</select>
</li>
</div>
You can pass another parameter:
#change="testChange($event, cat_id)"
testChange(event, catId){
console.log(event.target.value, catId);
}
Or access it inside the function:
testChange(event){
console.log(event.target.value, this.cat_id);
}

Why if I get the object property within the computed object gets undefined but not the object itself? Which approach fits better in this context?

My Greeting.
To put in context, my purpose of asking this question is to be able to render a child component inside a form based on the selected option of the <app-selector> Vue component as simple and silly as that.
For the sake of simplicity. I've made a snippet down here to expose what I'm trying to figure out.
Basically, the aim is to get the component name to be rendered by using the computed property cardTypeComponent. However, I want to fathom the way cardTypeComponent is working, since I cannot see why, in one hand, the first return (return this.form) is giving the object (this.form) with the property I want (card_type) but on the other hand the second return (return this.form.card_type ? this.form.card_type + 'Compose' : '') is giving me an empty string, assuming this.form.card_type is undefined when it is clear looking at the first return that, in fact, is not taking it as undefined.
There is way more context, since once the option is selected there is a validation process from the server before setting the value inside this.form object. Moreover, the form interaction is through steps, so once the user select the option he has to click a button to reach the form fields that corresponds to that type card selected, therefore the component is not going to be rendered the very first moment the user selects an option as in the snippet approach. However, it would entangle what I'm asking. Thanks beforehand.
It is better to use the Fiddle link below.
Snippet
var appSelector = Vue.component('app-selector', {
name: 'AppSelector',
template: `<div>
<label for="card_type">Card Type:</label>
<select :name="name" value="" #change="sendSelectedValue">
<option v-for="option in options" :value="option.value">
{{ option.name }}
</option>
</select>
</div>`,
props: {
name: {
required: false,
type: String,
},
options: {
required: false,
type: Array,
}
},
methods: {
sendSelectedValue: function(ev) {
this.$emit('selected', ev.target.value, this.name)
}
}
});
var guessByImageCompose = Vue.component({
name: 'GuessByImageComponse',
template: `<p>Guess By Image Compose Form</p>`
});
var guessByQuoteCompose = Vue.component({
name: 'GuessByQuoteComponse',
template: `<p>Guess By Quote Compose Form</p>`
});
new Vue({
el: '#app',
components: {
appSelector: appSelector,
guessByImageCompose: guessByImageCompose,
guessByQuoteCompose: guessByQuoteCompose,
},
data() {
return {
form: {},
card_types: [
{
name: 'Guess By Quote',
value: 'GuessByQuote'
},
{
name: 'Guess By Image',
value: 'GuessByImage'
}
],
}
},
computed: {
cardTypeComponent: function() {
return this.form; // return { card_type: "GuessByImage" || "GuessByQuote" }
return this.form.card_type ? this.form.card_type + 'Compose' : ''; // return empty string ("") Why?
}
},
methods: {
setCardType: function(selectedValue, field) {
this.form[field] = selectedValue;
console.log(this.form.card_type); // GuessByImage || GuessByQuote
console.log(this.cardTypeComponent); // empty string ("") Why?
}
},
mounted() {
console.log(this.cardTypeComponent); // empty string ("")
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form action="#" method="post">
<app-selector
:name="'card_type'"
:options="card_types"
#selected="setCardType"
>
</app-selector>
{{ cardTypeComponent }} <!-- Always empty string !-->
<component v-if="cardTypeComponent !== ''" :is="cardTypeComponent">
</component>
</form>
</div>
https://jsfiddle.net/k7gnouty/2/
You're setting a property on this.form which is not initialized first in data. This means you have run into Vue's change detection caveat. Use Vue.set when setting it:
methods: {
setCardType: function(selectedValue, field) {
Vue.set(this.form, field, selectedValue);
}
}
Alternatively, you could declare the properties first if that works better for you.

vuejs2 with select option get the value and other attribute

I'm using vuejs.
Let's say I have select and inside I have multiple options
like this:
<select v-model='line.unit' name='unit[]' required #change='change_unit($event)'>
<option v-for='(unit) in line.units' price='line.price' :value='unit.id'>#{{unit['get_unit_id']['name']}}</option>
<option selected class='selected' price='line.price' v-if='line.smallest_unit' :value='line.smallest_unit.id'>#{{line.smallest_unit['name']}}</option>
</select>
And this is the change_unit method:
change_unit:function($event)
{
}
How can I access the attribute price if I want the value of the selected option? I can get it like this ..
console.log(event.target.value);
But now can I access the price value attribute?
you could tweak the option value binding it to Vue.js
as you can see in this fiddle, which I'll explain here
Consider this HTML
<div id="app">
<select v-model="line.unit" name='unit[]' required>
<option v-for='(unit) in line.units' :value="unit">
{{unit.name}}
</option>
</select>
<h2 v-if="line.unit">
{{line.unit.price}}
</h2>
</div>
As you can see, I'm setting the <option> value binding it to unit, which is each single line.units item object. by doing that, selecting an option will actually set the v-model to unit, instead of an object's attribute
Consider this JS, in which I've created a hypotetic reproduction of your .data.
new Vue({
el: '#app',
data() {
return {
line: {
unit: {},
units: [{
id: 1,
price: 100,
name: 'foo'
},{
id: 2,
price: 200,
name: 'bar'
}]
}
}
}
})
Selecting an option will now show you it's price (I've put a <h2> as a demonstration)

VueJS 2.0 v-model dynamic target inside v-for

I've got a form with about 10 select elements built from an array in my Vue data.
The array of selectors is empty initially and then an AJAX call populates the array and Vue builds the HTML - I've kept the snippet below simplified just to demonstrate the issue I'm having with v-model
I want to create an object that has all the selected values in it, so I'm trying to use v-model="selected[ selector.name ]" as per the example below.
I want to easily be able to ask for selected.make or selected.fuel
Now this works if I initialize the selected property like this:
selected: { make: 'audi', fuel: 'petrol' }
If I leave it blank, like in the example, {}, then it doesn't get updated.
I don't want to manually hardcode all the properties of selected, I only want to be listing them once in the server side code that gets sent via AJAX
So am I missing something completely obvious, should I be doing this in a different way?
Maybe a method to find the dropdown that matches a field name and returns the value? Just that doesn't seem like a very Vue thing to do.
var app = new Vue({
el: '#example',
data: {
selectors: [
{
name: 'make',
options: ['audi','bmw']
},
{
name: 'fuel',
options: ['petrol','diesel']
}
],
selected: {}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="example">
<template v-for="selector in selectors">
<select v-model="selected[ selector.name ]">
<option v-for="option in selector.options">{{option}}</option>
</select>
</template>
<p>
{{selected.make}}
<br />
{{selected.fuel}}
</p>
</div>
it's probably becuase you're not setting new keys on an object with this.$set
try:
this.$set(this.selected, 'make', 'audi')
Not using this.$set - alias of Vue.set - will mean Vue doesn't set the new key as reactive, and in turn won't be watching for any updates to it, docs: https://v2.vuejs.org/v2/api/#vm-set
var app = new Vue({
el: '#example',
data: {
selectors: [{
name: 'make',
options: ['audi', 'bmw']
}, {
name: 'fuel',
options: ['petrol', 'diesel']
}],
selected: null,
},
created () {
// this would happen following your ajax request - but as an example this should suffice
this.selected = {}
this.selectors
.forEach((selector) => {
this.$set(this.selected, selector.name, '')
})
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="example">
<div v-if="selected">
<select v-model="selected[selector.name]" v-for="selector in selectors">
<option :value="option" v-for="option in selector.options">
{{option}}
</option>
</select>
<p>make: {{selected.make}}<p>
<p>fuel: {{selected.fuel}}</p>
<pre>{{ selected }}</pre>
</div>
</div>

Component Vuejs2 declare $data obj to share data between components

I am trying to make a Vue2 component to all the select of my app so would be easier later to change it if necessary!
I've based my research on the example given by the docs and I am breaking my head to figure out why should I speficy all the object on the data attr to make it work!
The following code is working properly, but if we change:
data: { record: { category_id: null } } by data: { record: {} } it stop to work!
Must be said the $data.record is loaded by ajax... would I always specify the whole object even knowing that after the ajax request I am going to replace all with something like this.record = response.data?
If somebody need there is FIDDLE [ https://jsfiddle.net/gustavobissolli/4xrfy54e/1/ ]
EDIT: SORRY GUYS JUST FIXED FIDDLE LINK
Vue.component('select2', {
props: ['options', 'value'],
template: '#select2-template',
data() {
return {
model: ''
}
},
mounted: function() {
this.model = this.value
},
watch: {
value: function(value) {
this.model = value
},
model: function(value) {
this.$emit('input', value)
},
}
})
var vm = new Vue({
el: '#el',
template: '#demo-template',
data: {
record: {
category_id: null
},
options: [{
id: 1,
text: 'Hello'
}, {
id: 2,
text: 'World'
}]
}
})
<div id="el"></div>
<!-- using string template here to work around HTML <option> placement restriction -->
<script type="text/x-template" id="demo-template">
<div>
<pre>{{ $data | json }}</pre>
<select2 :options="options" v-model="record.category_id" value="record.category_id"></select2>
</div>
</script>
<script type="text/x-template" id="select2-template">
<select v-model="model">
<option disabled>Select...</option>
<option v-for="opt in options" :value="opt.id">{{ opt.text }}</option>
</select>
</script>
So you are trying to edit a value which didn't arrive yet? :-)
The thing is: at the moment v-model="record.category_id" is "executed", you have nothing there, ie, there is no "category_id" at the "record" object. So, it binds to nothing. This is why the select won't work if you omit the "category_id" at data initialization.
But your assumption that when data arrives from server (ajax call) the component will not work, is wrong.
I have updated your fiddle: https://jsfiddle.net/4xrfy54e/4/
First, use the dropdown before clicking the button: since it is binded to nothing, it will not update anything. This is correct.
Now, click the button. The button is simulating that data arrived from the server, and is assigned to this.record of the vm.
Play with the dropdown again: since record.category_id exists now, the binding is working fine.
Please, read the "Reactivity in Depth" documentation page, and you will stop breaking your head :-)

Categories