Set initial v-model value when using v-for - javascript

I have a select that i want to populate with computed data, it works fine:
<select style="width: 175px">
<option>None</option>
<option v-for="label in labels">{{label.description}</option>
</select>
var vm = new Vue({
el: '#div',
data: {
},
computed: {
labels: function () {
//return labels based on some logic
}
}
});
However i want to bind the selected value to a property, so i change my code to:
<select style="width: 175px" v-model=selectedLabel">
<option>None</option>
<option v-for="label in labels">{{label.description}</option>
</select>
var vm = new Vue({
el: '#div',
data: {
selectedLabel: ''
},
computed: {
labels: function () {
//return labels based on some logic
}
}
});
Then i get no data at all in the select. It's obviously because i set selectedLabel to '', but there is no empty option in my select. But how can i fix this? I don't know what data that will be computed so i can't predefine selectedLabel.

Just set the default value of your selection option to '' like:
<div id="app">
<select style="width: 175px" v-model="selectedLabel">
<option value="">None</option>
<option v-for="label in labels" :value="label.value">{{label.description}}</option>
</select>
</div>
This will auto select the none option.
See this fiddle
var vm = new Vue({
el: '#app',
data: {
selectedLabel: ''
},
computed: {
labels: function () {
return [
{description:'test', value: 'test'},
{description: 'test1', value: 'test1'}
]
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<select style="width: 175px" v-model="selectedLabel">
<option value="">None</option>
<option v-for="label in labels" :value="label.value">{{label.description}}</option>
</select>
</div>

Related

how to pass value of selected value in select option to onchange function in methods vuejs?

I have a problem passing the selected option value id from select option to onchange function in methods. What i want to achieve here everytime i change select value in select option i would like to store the selected value to v-model=choosed and pass that value to methods onchange that choosed = item.id.
Here is my function in DeviceController to fetch devices:
public function devices()
{
try {
$devices = Device::orderby('id', 'asc')->get();
return response()->json($devices);
} catch (\Throwable $th) {
return response()->json(['message' => $th->getMessage()]);
}
}
Here is the function from method to get devices{} data from DeviceController:
getDevices() {
axios.get(`/api/devices`)
.then(response => {
this.devices = response.data;
});
},
Here is my select option code:
<select class="form-select form-select-lg mb-3" v-model="choosed" #change="onChange()">
<option :value="null">Choose Device to Send SMS</option>
<option v-for="item in devices" :key="item.id" :value="item.id" >{{ item.device_name }}
</option>
</select>
Here is my choosed v-model and devices json which devices that item.id came from in data:
export default {
data() {
return {
devices: {},
choosed: null,
}
},
Here is my onChange function in method:
onChange: function(){
this.choosed = this.item.id;
},
Try this...
https://codesandbox.io/s/vue-2-playground-forked-8yeqkx?file=/src/App.vue
<template>
<div id="app">
<select
class="form-select form-select-lg mb-3"
v-model="choosed"
>
<option :value="null">Choose Device to Send SMS</option>
<option v-for="item in devices" :key="item.id" :value="item.id">
{{ item.device_name }}
</option>
</select>
<p>Selected id: {{ choosed }}</p>
</div>
</template>
<script>
const devices = async () => {
await new Promise((r) => setTimeout(r, 2000));
return [
{
id: 0,
device_name: "Device A",
},
{
id: 1,
device_name: "Device B",
},
{
id: 2,
device_name: "Device C",
},
];
};
export default {
name: "App",
data() {
return {
choosed: null,
devices: [],
};
},
async mounted() {
this.$set(this, "devices", await devices());
},
};
</script>
Try this.
<template>
<div id="app">
<select
class="form-select form-select-lg mb-3"
#change="onChange($event)"
>
<option :value="null">Choose Device to Send SMS</option>
<option v-for="item in devices" :key="item.id" :value="item.id">
{{ item.device_name }}
</option>
</select>
<p>Selected id: {{ choosed }}</p>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
choosed: null,
devices: [
{
id: 1,
device_name: 'iphone'
},
{
id: 2,
device_name: 'android'
}
],
};
},
methods: {
onChange: function (event) {
this.choosed = event.target.value
}
}
};
</script>

Vue component $emit firing twice

I'm recently trying to reutilize my Vue components in some real-world application to remove unnecessary duplicates and clutter with <divs>.
But I'm having trouble in doing so. After hours I managed to "accomplish" it, but now the event fires twice and I don't know exactly why.
I've made a basic setup to show the problem:
Vue.component("bs-select",{
template:
`<div class="form-group">
<label class="control-label">{{ label }}</label>
<select2
ref="select2control"
:options="options"
:value="value"
#input="chosenOption"
></select2>
</div>`,
props: ["value", "label", "options"],
methods: {
chosenOption(val) {
this.$emit("input", val);
}
}
});
Vue.component("select2",{
template:
`<select :value="value" style="width: 100%">
<option value="" selected disabled>Choose...</option>
<option v-if="!options">{{ value }}</option>
<option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>`,
props: ["value", "options"],
mounted: function() {
const vm = this;
$(vm.$el)
.select2()
.on("change", function() {
console.log("CHANGE", vm.$el.value);
vm.$emit("input", vm.$el.value);
});
},
watch: {
value: function(val) {
$(this.$el)
.val(val)
.trigger("change");
}
}
});
new Vue({
el: "#app",
data: {
test: "bug",
options: [
{
value: "hello",
text: "Hello"
},
{
value: "bug",
text: "Bug"
}
]
}
})
* {
font-family: Arial;
font-size: 10px;
}
div {
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<bs-select v-model="test" :options="options"></bs-select>
<br><br>
<button #click="test = 'bug'">
Set 'test' variable to 'bug' (Two-way check)
</button>
{{ test }}
</div>
<div>
Event is firing twice in console...
</div>
I also Googled a lot and came to no conclusion on why this happens and/or how to fix this issue.
Any help is greatly appreciated.
After asking some of my friends, one figured it out that the "change" trigger must be in beforeUpdate.
So, the solved code looks like this:
Vue.component("bs-select",{
template:
`<div class="form-group">
<label class="control-label">{{ label }}</label>
<select2
ref="select2control"
:options="options"
:value="value"
#input="chosenOption"
></select2>
</div>`,
props: ["value", "label", "options"],
methods: {
chosenOption(val) {
this.$emit("input", val);
}
}
});
Vue.component("select2",{
template:
`<select :value="value" style="width: 100%">
<option value="" selected disabled>Choose...</option>
<option v-if="!options">{{ value }}</option>
<option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>`,
props: ["value", "options"],
mounted: function() {
const vm = this;
$(vm.$el)
.select2()
.on("change", function() {
console.log("CHANGE", vm.$el.value);
vm.$emit("input", vm.$el.value);
});
},
beforeUpdate() {
$(this.$el).val(this.value).trigger('change.select2')
}
});
new Vue({
el: "#app",
data: {
test: "hello",
options: [
{
value: "hello",
text: "Hello"
},
{
value: "solved",
text: "Solved"
}
]
}
})
* {
font-family: Arial;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script></script>
<div id="app">
<bs-select v-model="test" :options="options"></bs-select>
<br><br>
{{ test }}
<br><br>
<button #click="test = 'solved'">
Set 'test' variable to 'solved'
</button>
</div>
It works quite nice, but he also suggested me to use this approach, which is a lot cleaner. I am currently using that now, but I leave the original answer to the question too, in case someone needs it.
I'm not sure what exactly you are trying to do here, but my guess is you were firing the onchange event twice, once when it actually changed and once in the watcher. Anyways you don't really need to use listeners like that when there are vue solutions available like this:
<div id="app">
<bs-select :value="test" v-on:change-value="test = $event" :options="options"></bs-select>
<br><br>
{{ test }}
<br><br>
<button #click="test = 'bug'">
Set 'test' variable to 'bug'
</button>
</div>
<div>
Event is firing twice in console...
</div>
component:
Vue.component("bs-select",{
template:
`<select :value="value" v-on:change="changeVal" style="width: 100%">
<option value="" selected disabled>Choose...</option>
<option v-if="!options">{{ value }}</option>
<option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>`,
props: ["value", "options"],
methods: {
changeVal: function(event) {
this.$emit('change-value', event.target.value)
}
}
});
new Vue({
el: "#app",
data: {
test: "bug",
options: [
{
value: "hello",
text: "Hello"
},
{
value: "bug",
text: "Bug"
}
]
}
})
https://jsfiddle.net/kv3bq1dw/
For anyone coming across this now, this is the proper way to get it to fire only once.
<template>
<select><slot></slot></select>
</template>
<script>
module.exports = {
props: ['options', 'value'],
mounted: function () {
console.log(this.options);
var vm = this;
$(this.$el)
// init select2
.select2({
data: this.options,
theme: 'bootstrap',
width: '100%',
placeholder: 'Select',
allowClear: true
})
.val(this.value)
.trigger('change')
// emit event on change.
.on('select2:select', function () { // bind to `select2:select` event
vm.$emit('input', this.value)
});
},
watch: {
value: function (value) {
// update value
$(this.$el)
.val(value)
.trigger('change');
},
options: function (options) {
// update options
$(this.$el).empty().select2({ data: options })
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
}
</script>

How to make Vue component work with v-model preserving value type?

Problem
When I'm having a v-model on a HTML <select>, v-model is setting the given property to selected value preserving types of that values - if I bind a number to <option>, model property is set to number, if I bind an Object it's set to that Object.
<script>
export default {
data: {
options: [5, 10, 15, 'text', { 'description': 'I am an Object' }],
}
};
</script>
<template>
<select v-model="model">
<option
v-for="option in options"
:value="option"
>
{{ option }}
</option>
</select>
<template>
I'm having a <base-select> custom component, which is wrapping the usage of <select> tag for me. I'm trying to implement the same v-model behavior for it, but am failing, because the types are not preserved - I'm returned Strings all the time, even when I'm binding numbers or objects.
//// BaseSelect.vue
<script>
export default {
props: {
options: {
type: Array,
required: true
},
value: {
required: true
}
},
};
</script>
<template>
<select
:value="value"
#input="$emit('input', $event.target.value)"
>
<option
v-for="option in options"
:value="option"
>
{{ option }}
</option>
</select>
</template>
//// App.vue
<script>
#import 'BaseSelect' from './BaseSelect';
export default {
components: {
BaseSelect,
},
data: {
options: [5, 10, 15, 'text', { 'description': 'I am an Object' }],
}
};
</script>
<template>
<base-select
v-model="model"
:options="options"
/>
<template>
Fiddle
Here is where this behaviour is clearly visible: http://jsfiddle.net/4o67pzLs/14/
The first select is preserving types of values bound to model, while the other one is all the time setting values to Strings.
Question
Is it possible to implement v-model on custom component, which would preserve types? If so, how?
Here's how we finally did it along with #RobertKusznier :
Bind the select with a computed property of the component using v-model
Define a getter and setter for that computed property
The getter returns the value of the component
The setter emits the change event
It preserves the type of the selected option's value and doesn't mutate the component's value.
Credit goes to #RobertKusznier who suggested we didn't mutate the value of the component.
let baseSelect = {
props: {
options: {
type: Array,
required: true
},
value: {
required: true
}
},
computed: {
valueCopy: {
get() {
return this.value;
},
set(value) {
this.$emit('input', value);
}
}
},
template: `
<select
v-model="valueCopy"
>
<option
v-for="option in options"
:value="option"
>
{{ option }}
</option>
</select>
`,
};
new Vue({
el: '#app',
components: {
baseSelect
},
data: {
model: 5,
options: [5, 10, 15, 'text', new Date()]
},
template: `
<div>
<select v-model="model">
<option
v-for="option in options"
:value="option">
{{ option }}
</option>
</select>
<base-select
v-model="model"
:options="options"
:sister="10"
/>
<p>model: {{ model }}</p>
<p>typeof model: {{ typeof model }}</p>
</div>
`
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app"></div>

vue.js v-bind:value is not working with select option form element

I have a strange problem, watching at the tutorials of vue.js here: https://v2.vuejs.org/v2/guide/forms.htmlthe the following code should work:
<div class="input-field">
<select v-model="selected">
<option v-for="couponType in couponTypes" v-bind:value="couponType" value="">{{ couponType }}</option>
</select>
<label>Tipo de cupon</label>
</div>
this template works with the following script:
<script>
export default {
data: function () {
return {
couponTypes: [ "V333333333333é",
"Vasdasdasd",
"V211111111Café",
"444444444444444444"
],
selected: "",
newCoupon: {
couponTypeSelected: "",
userId: ""
}
}
},
methods: {
SendCoupon: function () {
console.log(this.newCoupon)
console.log(this.selected)
}
},
created: function () {
$(document).ready(function() {
$('select').material_select();
$('.modal').modal();
});
}
}
When sendCoupon() is triggered it supposedly selected variable should print the value of the selected option in the select element, but it only prints an empty string that is the initial setted value.
I cannot reproduce your issue. Adding a button with a click event that calls your SendCoupon() method clearly demonstrates that each selected item is correctly output. See this working JSFiddle.
Template:
<div id="app">
<div class="input-field">
<select v-model="selected">
<option v-for="couponType in couponTypes" v-bind:value="couponType" value="">
{{ couponType }}
</option>
</select>
<label>Tipo de cupon</label>
<button #click="SendCoupon">Send</button>
</div>
</div>
JavaScript:
new Vue({
el: '#app',
data: function () {
return {
couponTypes: [ "V333333333333é",
"Vasdasdasd",
"V211111111Café",
"444444444444444444"
],
selected: "",
newCoupon: {
couponTypeSelected: "",
userId: ""
}
}
},
methods: {
SendCoupon: function () {
console.log(this.newCoupon)
console.log(this.selected)
}
},
created: function () {
}
})
Note that it is the selected property that is updated, not the newCoupon property since your select v-model is bound to the selected property.
After days searching about a solution I found that the cause of this error is the use of materializecss with vue.js on the templates. According to this reported issue ( github reported isssue ), materialize css modify the DOM when there is a select or ul (list) on the template of a .vue component. In the reported issue on Github there is a workaround: add browser-default as a class to the select element,this disables to materialize to modify the DOM element, then binding of vue could work. Here I drop an example.
<div class="input-field">
<select class="browser-default" id="selectoption" v-model="newCoupon.coupon">
<option v-for="selected in couponTypes" v-bind:value="selected">{{ selected }}</option>
<label for="selectoption">Cupon</label>
</select>
</div>

How to get the text of the selected option using vuejs?

I want to get the text of a selected option input and display it somewhere else. I know how to do it using jQuery but I want to know how can we do it using Vuejs.
Here is how we do in jQuery. I mean the text of Selected Option not the value.
var mytext = $("#customerName option:selected").text();
Here is my HTML
<select name="customerName" id="">
<option value="1">Jan</option>
<option value="2">Doe</option>
<option value="3">Khan</option>
</select>
{{customerName}}
Now how can I display the selected option under it. like Jan, Doe, Khan ?
Instead of define the value only as the id, you can bind the selected value with an object with two attributes: value and text.
For example with products:
<div id="app">
<select v-model="selected">
<option v-for="product in products" v-bind:value="{ id: product.id, text: product.name }">
{{ product.name }}
</option>
</select>
</div>
Then you can access to the text through the "value":
<h1>Value:
{{selected.id}}
</h1>
<h1>Text:
{{selected.text}}
</h1>
Working example
var app = new Vue({
el: '#app',
data: {
selected: '',
products: [
{id: 1, name: 'A'},
{id: 2, name: 'B'},
{id: 3, name: 'C'}
]
}
})
<div id="app">
<select v-model="selected">
<option v-for="product in products" v-bind:value="{ id: product.id, text: product.name }">{{ product.name }}
</option>
</select>
<h1>Value:
{{selected.id}}
</h1>
<h1>Text:
{{selected.text}}
</h1>
</div>
<script src="https://unpkg.com/vue#2.4.4/dist/vue.js"></script>
I had this issue, where I needed to get a data attribute from a selected option, this is how I handled it:
<select #change="handleChange">
<option value="1" data-foo="bar123">Bar</option>
<option value="2" data-foo="baz456">Baz</option>
<option value="3" data-foo="fiz789">Fiz</option>
</select>
and in the Vue methods:
methods: {
handleChange(e) {
if(e.target.options.selectedIndex > -1) {
console.log(e.target.options[e.target.options.selectedIndex].dataset.foo)
}
}
}
But you can change it to get innerText or whatever. If you're using jQuery you can $(e).find(':selected').data('foo') or $(e).find(':selected').text() to be a bit shorter.
If you are binding a model to the select element, it will only return the value (if set) or the contents of the option if there is no value set (like it would on submitting a form).
** EDIT **
I would say that the answer #MrMojoRisin gave is a much more elegant way of solving this.
The below code worked to get the Text from the selected option. I added a v-on:change , which calls a function onChange() to the select element.
methods:{
onChange: function(e){
var id = e.target.value;
var name = e.target.options[e.target.options.selectedIndex].text;
console.log('id ',id );
console.log('name ',name );
},
<select name="customerName" id="" v-on:change="onChangeSite($event)">
<option value="1">Jan</option>
<option value="2">Doe</option>
<option value="3">Khan</option>
</select>
Assuming you have a customers list and a selected customer on your model, an example like below should work perfect:
<select v-model="theCustomer">
<option :value="customer" v-for="customer in customers">{{customer.name}}</option>
</select>
<h1>{{theCustomer.title}} {{theCustomer.name}}</h1>
I guess your values should be in the JS. Then you can easily manipulate it. Simply by adding:
data: {
selected: 0,
options: ['Jan', 'Doe', 'Khan']
}
Your markup will be cleaner:
<select v-model="selected">
<option v-for="option in options" value="{{$index}}">{{option}}</option>
</select>
<br>
<span>Selected: {{options[selected]}}</span>
Here is the update JSFiddle
As th1rdey3 pointed out, you might want to use complex data and values couldn't simple be array's indexes. Still you can use and object key instead of the index. Here is the implementation.
You can use Cohars style or you can use methods too. Data is kept in options variable. The showText method finds out the selected values text and returns it. One benefit is that you can save the text to another variable e.g. selectedText
HTML:
<div id='app'>
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<br>
<span>Selected: {{ showText(selected) }}</span>
</div>
JAVASCRIPT:
var vm = new Vue({
el: '#app',
data: {
selected: 'A',
selectedText: '',
options: [{
text: 'One',
value: 'A'
}, {
text: 'Two',
value: 'B'
}, {
text: 'Three',
value: 'C'
}]
},
methods: {
showText: function(val) {
for (var i = 0; i < this.options.length; i++) {
if (this.options[i].value === val){
this.selectedText = this.options[i].text;
return this.options[i].text;
}
}
return '';
}
}
});
JSFiddle showing demo
I tried to use the following suggestion of MrMojoRisin's
v-bind:value="{ id: product.id, text: product.name }"
However for me this was resulting in that Object's toString() representation being assigned to value, i.e. [object Object]
What I did instead in my code was to call JSON.stringify on a similar object bound to the value:
v-bind:value="JSON.stringify({id: lookup[lookupIdFields[detailsSection]], name: lookup.Name})"
Then of course I can convert it back to an object using JSON.parse and get the requisite id and name values from the result
الفترة
اختر الفترة
{{ period.name }}
<label for="period_id" class="block font-medium text-sm text-gray-700">الفترة</label>
<select v-model="form.period_id" id="period_id" class="border-gray-300">
<option disabled value="">اختر الفترة</option>
<option v-for="period in periods" :value="period.id" :selected="building.period_id === period.id ? 'selected' : null">
{{ period.name }}
</option>
</select>
Outside of the template I access the name of the option like this:
let option_name = this.options[event.target.options.selectedIndex].name
To do this take this approach to set up your template:
Defined your options in an array like
[{"name":"Bar","value":1},{"name":"Baz","value":2}] ... etc
Put your options in the data function of the component
<script>export default {function data(){return { options }}}</script>
Load the options in the template using v:for:
<option v-for="option in options" v-bind:value="option.value">{{option.name}}</option>
Use #change="getOptionName" on the select element:
<select #change="getOptionName">
In your methods get the name:
getOptionName(event){ let option_name = this.options[event.target.options.selectedIndex].name }
Note in this case the object options in event.target.options does not have anything to do with your data named options ... hopefully that will avoid confusion
So a more advanced approach, but I believe when it is all set up correctly getting the name is not terrible, although It could be easier.
You can find it out in the Vue documentation here : http://vuejs.org/guide/forms.html#Select
Using v-model :
<select v-model="selected">
<option selected>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>
In your case, I guess you should do :
<select name="customerName" id="" v-model="selected">
<option>Jan</option>
<option>Doe</option>
<option>Khan</option>
</select>
{{selected}}
Here is a working fiddle : https://jsfiddle.net/bqyfzbq2/
I think some of the answers here are a bit too complex, so I'd like to offer an easier solution. I'm only going to use an event handler in the example and leave out things like model binding, etc.
<select #change="yourCustomMethod">
<option value="1">One</option>
<option value="2">Two</option>
</select>
Then in your Vue instance:
...
methods: {
yourCustomMethod: function(event) {
var key = event.target.value, // example: 1
value = event.target.textContent; // example: One
}
}
...

Categories

Resources