Fire Event When Model Value Change in Vue JS - vue.js

I have a simple drop down like this:
<select v-model="selected.applicationType"
v-on:change="applicationTypeChanged"
class="form-control">
<option v-for="item in applicationTypes"
v-html="item.text"
v-bind:value="item.value"></option>
</select>
The drop down is bound to a model and change event is also working as expected. But programatically I am changing the value of selected.applicationType and then I need to fire the change event of the drop down. How can I fire change event when model value is changed?

You can use a watcher for this
<select v-model="selected.applicationType" class="form-control">
<option v-for="item in applicationTypes"
v-html="item.text"
v-bind:value="item.value"></option>
</select>
export default
{
data()
{
return {
selected:
{
applicationType: null
}
}
},
watch:
{
'selected.applicationType'(newVal)
{
this.applicationTypeChanged(newVal);
}
},
methods:
{
applicationTypeChanged(newValue)
{
...
}
}
}

Related

Save selected values of input despite switching between two components in VUEJS

So I have two components that are imported into my app.vue:
<script>
import Leaderboard from "./components/Comp1.vue";
import Search from "./components/Comp2.vue";
export default {
name: "App",
components: {
Comp1,
Comp2,
},
}
These components are called, when I click on the corresponding button. This all works fine.
But in the components I have some input fields such as in Comp1.vue:
<template>
<div>
<select
class="form-select"
name="event"
id=""
v-model="selectedEvent"
>
<option value="">Please choose an event:</option>
<option v-for="event in eventsList" :key="event">
{{ event }}
</option>
</select>
</div>
</template>
<script>
data: function () {
return {
selectedEvent: "",
</script>
Here I can choose, which event to watch. But after switching to Comp2 and then again choosing Comp1, the selectedEvent is empty. Obviously, because its defined empty in data.
Is there any way to store the selected value in a session variable or would you prefer a different technique?
UI looks like this:
You can maintain an Object in your parent which you can pass as props to a props and then have a two way handshake
<Leaderboard :formInputs="formInputs"></Leaderboard>
<script>
import Leaderboard from "./components/Comp1.vue";
import Search from "./components/Comp2.vue";
export default {
name: "App",
components: {
Comp1,
Comp2,
},
data() {
return {
formInputs: {
compOneInput: '',
compTwpInput: ''
}
},
methods: {
updateData(payload) {
this.formInputs[payload.key] = payload.value;
}
}
and then pass this formInputs to your child Component from where you
you can emit the change whenever you update the input inside that
<template>
<div>
<select
class="form-select"
name="event"
id=""
v-model="selectedEvent"
>
<option value="">Please choose an event:</option>
<option v-for="event in eventsList" :key="event">
{{ event }}
</option>
</select>
</div>
</template>
<script>
export default {
data: function () {
return {
selectedEvent: this.formInputs.compOneInput ? this.formInputs.compOneInput : '',
}
},
watch: {
formInputs(newVal) {
this.selectedEvent = newVal.compOneInput;
},
selectedEvent(newVal, oldVal) {
if(newVal !== oldVal) {
this.$emit('updateData', {key: compOneInput, value: this.selectedEvent});
}
}
}
props: {
formInputs: Object
}
}
</script>
Using the above example for component one , you can implement the same for component two also
you can add a watcher on selectedEvent then store the data in vuex store

VueJS Using #click on <option> elements and #change on <select> element

I have a simple function that manipulates a data between true and false. I use it to make my div hidden and visible like a toggle. I also have a with three element. I found out that I cannot use #click for <option> elements, I need to use #change for my <select>.
But in this way, whenever an is selected, the function is being triggered and my data toggles between true and false. Here is my <select> element;
<select #change="isDisabled">
<option>Please select a security type</option>
<option>No Security</option>
<option>Personal</option>
<option>Enterprise</option>
</select>
IsDisabled function takes a variable and change its values between true and false so my div becomes hidden and visible as follows;
<div v-if="noSecurity">something</div>
But here is the thing, I only want to trigger the function when the user select the "No Security" option. Now it's being triggered whenever I select an option, so it turned out to be some kind of a toggle. But I want to hide the div when I select the "No Security" option and show the div if something different is selected. What should I do?
I've made a CodeSandbox where you could see the result :
https://codesandbox.io/s/magical-meitner-63eno?file=/src/App.vue
But here is the explanation:
<template>
<section>
<select #change="isDisabled">
<option>Please select a security type</option>
<option>No Security</option>
<option>Personal</option>
<option>Enterprise</option>
</select>
<div v-if="noSecurity">You Choose no security, that's dangerous !</div>
</section>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
data() {
return {
noSecurity: false,
};
},
methods: {
isDisabled(e) {
console.log("e", e.target.value);
if (e.target.value === "No Security") {
// do your change
return (this.noSecurity = !this.noSecurity);
}
// to allow reset if another option is selected
if (this.noSecurity) {
return this.noSecurity = false;
}
},
},
};
</script>
Basically when you use the #change handler, your function will receive an event, in this event you can catch the target value with event.target.value.
Doing so, you do a condition if the value is equal to No Security (so the selected item), you change your state, if it's not No Security, you do nothing, or you do something else you would like to do.
Appart from that, I advice you to change your method name isDisabled to a global convention name like handleChange, or onChange.
Pass id values in your option so when you get the select event you're clear that No security or whatver the name you would like to change will be the same.
Because if one day you change No security to another name, you have to update all your conditions in your app. Try to avoid conditions with strings values like this if you can.
<option value="1">No Security</option> // :value="securityType.Id" for example if coming from your database
<option value="2">Personal</option>
<option value="3">Enterprise</option>
then in your function it will be
if (e.target.value === noSecurityId) {
// do your change
this.noSecurity = !this.noSecurity;
}
//...
There's no need for the additional noSecurity variable. Create your select with v-model to track the selected value. Give each option a value attribute.
<select v-model="selected">
<option value="">Please select a security type</option>
<option value="none">No Security</option>
<option value="personal">Personal</option>
<option value="enterprise">Enterprise</option>
</select>
Check that value:
<div v-if="selected === 'none'">something</div>
You can still use the noSecurity check if you prefer by creating a computed:
computed: {
noSecurity() {
return this.selected === 'none';
}
}
Here's a demo showing both:
new Vue({
el: "#app",
data() {
return {
selected: ''
}
},
computed: {
noSecurity() {
return this.selected === 'none';
}
},
methods: {},
created() {}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select v-model="selected">
<option value="">Please select a security type</option>
<option value="none">No Security</option>
<option value="personal">Personal</option>
<option value="enterprise">Enterprise</option>
</select>
<div v-if="selected === 'none'">something</div>
<div v-if="noSecurity">something</div>
</div>
Is using v-model instead of using a method is option for you? If it is, please try the following:
HTML:
<div id="hello-vue" class="demo">
<select v-model="security">
<option>Please select a security type</option>
<option>No Security</option>
<option>Personal</option>
<option>Enterprise</option>
</select>
<div v-if="security=='No Security'">something</div>
</div>
JS:
const HelloVueApp = {
data() {
return {
security: undefined
}
}
}

Vuelidate check box validation

I am using Vue.js and I am new on it. I am currently working on validation. I had used vuelidate as my validation library. I had successfully done form validation, but trouble came when I had to check validation for check box.
How can I check validation for check box? Also, I had used bootstrapvue to display check box.
<b-col lg="6" md="6" sm="6">
<label>Bus Route</label>
<b-form-group>
<b-form-checkbox v-for="route in busRouteList"
v-model.trim="selectedRoute"
v-bind:value="route.value"
v-bind:unchecked-value="$v.selectedRoute.$touch()">
{{route.text}}</b-form-checkbox>
</b-form-group>
<div class="form-group__message" v-if="$v.selectedRoute.error && !$v.selectedRoute.required">
Field is required
</div>
</b-col>
validations: {
selectedRoute: {
required
},
}
As false is also valid value so, you should try to use sameAs
import { sameAs } from 'vuelidate/lib/validators'
terms: {
sameAs: sameAs( () => true )
}
You should bind #change methods:
<b-form-checkbox v-for="route in busRouteList"
v-model.trim="selectedRoute"
v-bind:value="route.value"
#change="$v.selectedRoute.$touch()">
and you might want to use custom function:
selectedRoute: {
checked (val) {
return val
}
},
This worked for me.
Basically, you need to make its value 'sameAs' a boolean 'true', which means the checkbox is checked.
So, i.e:
privacyCheck: {
sameAs: sameAs(true)
},
I hope this little example will help you to understand how to validate a checkbox.
You have to check when the input is changing. I recommend you to use #change.
In template
<div class="input">
<label for="country">Country</label>
<select id="country" v-model="country">
<option value="usa">USA</option>
<option value="india">India</option>
<option value="uk">UK</option>
<option value="germany">Germany</option>
</select>
</div>
<div class="input inline" :class="{invalid: $v.terms.$invalid}">
<input type="checkbox" id="terms" v-model="terms" #change="$v.terms.$touch()">
<label for="terms">Accept Terms of Use</label>
</div>
So the terms will be valid if selected country will be germany.
validations: {
terms: {
checked(val) {
return this.country === "germany" ? true : val;
}
}
}
of course country, terms are defined in data():
country:'',
terms: false
`
validations: {
terms: {
checked: (val) => {return val;}
}
}
`
With vuelidate-next (for both Vue 2 and Vue 3 support) it's so simple as using sameAs built-in validator with true as a direct parameter. For example, when using inside a setup method:
const termsAccepted = ref(false)
const v$ = useVuelidate(
{ termsAccepted: { sameAs: sameAs(true) } },
{ termsAccepted }
)
return { v$, termsAccepted }

Change options and value at the same time

I have a problem when I try to change the options of my select and its value at the same time. If I use v-model, it works properly but if I use v-bind:value + v-on:change, it will not work.
Here is a js fiddle that will illustrate the problem : https://jsfiddle.net/2vcer6dz/18/
The first time you click on the button "change", only the first select value will be 3. If you reclick they all become 3.
Html
<div id="app">
<select v-model="value">
<option v-for="item in options" :key="item.Value" :value="item.Value">{{item.Text}}</option>
</select>
<select :value="value" v-on:change="value = $event.target.value">
<option v-for="item in options" :key="item.Value" :value="item.Value">{{item.Text}}</option>
</select>
<select-option v-model="value" :options="options"></select-option>
<br />
<input type="button" value="change" v-on:click="change" />
</div>
<template id="template-select-option">
<select :value="value" v-on:change="update($event.target.value)">
<option v-for="item in options" :key="item.Value" :value="item.Value">{{item.Text}}</option>
</select>
</template>
Javascript
Vue.component('select-option', {
template: '#template-select-option',
props: ['value', 'options'],
methods: {
update: function (value) {
this.$emit('input', value);
}
}
});
new Vue({
el: '#app',
data: {
value: 1,
options: [{Value:1, Text:1}, {Value:2, Text:2}]
},
methods: {
change: function () {
this.options = [{Value:1, Text:1}, {Value:2, Text:2}, {Value:3, Text:3}];
this.value = 3;
}
}
});
Expected result
All selects should have the value "3" when you click on the button "change"
Changing the options and the value at the same time is confusing Vue. This is probably a minor bug in Vue. If you use $nextTick to push the value change off to the next update cycle, they all work.
change: function () {
this.options = [{Value:1, Text:1}, {Value:2, Text:2}, {Value:3, Text:3}];
this.$nextTick(() => {
this.value = 3;
});
}
It seems that this is a known bug which was closed because a workaround was found.
The workaround is to declare another property and cast v-model on it. This solutions is easier to implement inside a component.
https://jsfiddle.net/6gbfhuhn/8/
Html
<template id="template-select-option">
<select v-model="innerValue">
<option v-for="item in options" :key="item.Value" :value="item.Value">{{item.Text}}</option>
</select>
</template>
Javascript
Vue.component('select-option', {
template: '#template-select-option',
props: ['value', 'options'],
computed: {
innerValue: {
get: function() { return this.value; },
set: function(newValue) { this.$emit('input', newValue); }
}
}
});
Note: In the github thread, it is suggested to use a computed property instead, but if you use a computed property, vue will throw warning every time you change the value in your dropdown because computed property don't have setter.

Pass selected value to vuejs function

How can I pass selected value to a vuejs function?
v-model won't help I guess.
I need to set values for the filter after
item: items | orderBy sortKey reverse
where reverse and sortKey are dynamic values.
html
<select class="sort-filter" v-on="change: sortBy(???)">
<option value="title asc">Title (A-Z)</option>
<option value="title desc">Title (Z-A)</option>
<option value="price asc">Price (Min. - Max.)</option>
<option value="price desc">Price (Max. - Min.)</option>
</select>
js
methods: {
sortBy: function (sortKey) {
console.log(sortKey)
}
}
You have several ways to do it.
Edit: Improved 2)
It is possible to use v-model just like in 2) but instead of using the value directly in your orderBy filter, you can use computed properties
computed: {
sortKey: {
get: function() {
return this.sorting.split(' ')[0]; // return the key part
}
},
sortOrder: {
get: function() {
return this.sorting.split(' ')[1]; // return the order part
}
}
}
This way, sortKey and sortOrder will be available like a normal property in you filter:
v-repeat="items | orderBy sortKey sortOrder"
1) Use javascript event:
If you don't specify any parameter, the native event object will be passed automatically
<select class="sort-filter" v-on:change="sortBy">
You can then use it like this:
methods: {
sortBy: function(e) {
console.log(e.target.value);
},
}
2) Using v-model
You can add the v-model directive
<select name="test" v-model="sorting" v-on:change="sortBy">
This way the sorting value will be updated on every change.
You can add this value in the data object of you ViewModel to be more clear:
data: {
sorting: null // Will be updated when the select value change
}
You can then access the value like in your method:
methods: {
sortBy: function() {
console.log(this.sorting);
},
}
If you just need to update the sortKey value, this method is not even necessary.
3) Other weird way
You can apparently use your model value as a parameter.
<select name="test" v-model="sortKey" v-on:change="sortBy(sortKey)">
This is working but I don't really see the point.
methods: {
sortBy: function(sortKey) {
console.log(sortKey);
},
}
This should work if you want to loop with dynamic values and can not use v-model
<select name="option" v-on:change="selectedOption($event.target.value)">
<option value="0">SELECT</option>
<option v-for="i in 10" :value="i">{{ i }}</option>
</select>
If you want to pass selected value to a vuejs function, Please do the following :
you need to use v-model directive in select tag as v-model = variableName
pass that variable as parameter like #on-change=sortBy(variableName);
So your code will look like :
<select class="sort-filter" v-model="sortingVariable" #on-change="sortBy(sortingVariable)">
<option value="title asc">Title (A-Z)</option>
<option value="title desc">Title (Z-A)</option>
<option value="price asc">Price (Min. - Max.)</option>
<option value="price desc">Price (Max. - Min.)</option>
</select>
try
<select name="option" #change="myFunction($event.target.value)">
<option v-for="item in list" :value="item.code" :key="item.code">{{ item.name }}</option>
</select>
// Function
myFunction(value) {
console.log(value);
}