Value is changed just in methods, VueJs - vue.js

I have a value with false default value, and inside a watch (for another value) I have chage it's value, but the value is changed just there, on second Click (event), the value is changed. Not sure why..
The value is change when I trigger that event twice..
<template>
<h1>{{hide_confidence}}</h1> //HERE IS NOT CHANGED
</template>
...
data() {
return {
mainSelect: '',
hide_confidence: true
};
},
watch: {
mainSelect(value) {
if (value !== "" && value !== "none") {
this.hide_confidence = false;
// this.confidence_score = "";
} else {
this.hide_confidence = true;
}
console.log("Value: ", this.hide_confidence); //HERE IS CHANGED
},
hide_confidence(value{
console.log("Value: ", value); //HERE IS NOT CHANGED
}
}
...

watch is not a method to change your data permanently. Just you can play with other properties with changing it temporary.
If you want to change a value permanently then you have to call an event.
Please take look in details here:
https://v2.vuejs.org/v2/guide/computed.html#Computed-vs-Watched-Property

Related

watch computed properties in vuejs

I am trying to watch my computed property isEllipsisActive() to see if the value is true or false and then I would like to set shouldShowArrow to this value.
The value will changed when the user resizes their browser based on the condition this.wrap.scrollHeight < this.h1.scrollHeight;,
Currently it works but only if I refresh the browser, I need it to update when value changes.
How can I watch if the value of isEllipsisActive() changes?
export default {
data() {
return {
h1: null,
wrap: null,
shouldShowArrow: false,
};
},
isEllipsisActive() {
if (!this.wrap && !this.h1) {
console.log("Not initialized", 'not initalized');
return false;
}
return this.wrap.scrollHeight < this.h1.scrollHeight;
},
},
mounted() {
this.$nextTick(() => {
this.h1 = this.$refs.h1;
this.wrap = this.$refs.wrap;
});
},
watch: {
isEllipsisActive(newValue) {
this.h1 !== null && console.log('changed')
},
},
};
You can’t “watch” a computed value because a computed value is already dynamic.
If you want to perform some logic basic on a computed value, then just use it to do so:
<h1 v-if="isEllipsisActive">{{ title }}</h1>
You don’t need to “watch” your computed value just to set yet another boolean.
you can try this way and see if trigger the watch for you
watch: {
isEllipsisActive: {
deep: true
handler(now){
this.h1 !== null && console.log('changed')
}
}
since your computed value is in the same component that you are trying to watch the change performed you should not need to do that. Or you can watch one of the values and perform all the logic you need inside the watcher. But As #Dan say in another comment, sometimes we need to watch those computed values. I use this logic when I want to execute extra code after the computed getter from Vuex trigger changes.

How can i get state of a checkbox whether is checked or no not in vuejs

<input class="checkout-item" :value="cart_food.id" #change="sumOfProduct(cart_food.quantity,cart_food.variation.price)" name="cart_item[]" v-model="checkbox" type="checkbox">
sumOfProduct(price,quantity) {
this.productPrice = price;
this.productQuantity = quantity;
var total = (this.productPrice * this.productQuantity);
this.totalPrice.push(total);
this.totalPrice.pop(total);
var sum = this.totalPrice.reduce(function(a, b)
{
return a + b;
}, 0);
console.log(sum);
this.finalPrice = sum;
},
In my sumOfProduct method i want to verify the checkbox whether it is checked or not. Any idea how can i achive this.
You put an v-model directive to your checkbox input, named "checkbox" . I assume you also included the same as reactive data prop like :
<script>
export default {
data() {
return {
checkbox : false
}
}
}
</script>
If that is the case , you can simple check this.checkbox for true or false.
sumOfProduct(price,quantity) {
console.log(this.checkbox);
}
If you also want to checkout the native "change" event inside your method, include $event as first argument when you call it.
v-on:change="sumOfProduct($event, 2, 1.0)"
sumOfProduct(event, price, quantity) {
console.log(event.target.checked);
// false if unchecked, true if checked
}
https://codesandbox.io/s/frosty-sky-5r7ii?fontsize=14&hidenavigation=1&theme=dark

Move Vue form input validation in component into a method

I have a Vue componenet for my input field. I have added some validation that makes sure only numbers are added. I added this on the oninput.
I'd like to move this to a method so I can add more checks (eg. if Type !== number)
This works well, but with the validation inline:
<input
v-bind="$attrs"
v-on="{
...$listeners,
input: event => $emit('input', event.target.value)
}"
oninput="this.value = Math.abs(this.value)"
/>
This is how I would like it (but current the validation is not working):
<input
v-bind="$attrs"
v-on="{
...$listeners,
input: event => handleInput(event.target.value)
}"
/>
methods: {
handleInput(value) {
console.log(value);
// 1st emit
this.$emit("input", value);
// 2nd Validate -- Not working...
this.value = Math.abs(this.value);
}
}
Any ideas on how I get this.value = Math.abs(this.value); to feed back into the input?
UPDATE
Thanks to a helpful comment I made some progress. The below code works for the first character but not for ongoing characters.
If numbers are typed, then validation passes true and input emitted.
If 1 character (eg. a) is typed then we emit the number 0. If a second character is inputted then the char is emitted (eg. press b and now the input is 0b)
I can see the this.$emit("input", 0) is triggered, so not sure why char emitted.
methods: {
validateInput(value) {
// if it type isnt set as a number then leave
if (this.type != "number") {
return true;
}
// check if value a number
if (Math.abs(value)) {
return true;
}
return false;
},
handleInput(value) {
if (this.validateInput(value)) {
this.$emit("input", value);
} else {
this.$emit("input", 0);
}
}
}
If you want to check a value before emitting the input event, you could do it like this:
methods: {
validateInput(value) {
if (typeof value !== 'number') { return false; } // check if it's not a string
if (value !== Math.abs(value)) { return false; } // check if value is positive
return true
}
handleInput(value) {
if (this.validateInput(value)) { this.$emit("input", value); }
this.$emit("input") // if value is not a valid input, you may want to do nothing, or emit merely that the event happened.
}
}
A better way of doing a custom input would be to use the value prop of an input, and bind it to a dynamic property in your component, for example by using v-model="value". Fun fact: v-model has a modifier v-model.number which would do exactly what you need.
The only caveat is that you can't directly modify props, so you'd need to use a computed property as a way to automatically handle the 'getting and setting' of your form's value.
// CustomInput.vue
<template>
<input v-bind="$attrs" v-on="$listeners" v-model.number="localValue" />
</template>
<script>
export default {
props: {
value: {
type: Number,
required: true,
}
}
computed: {
localValue: {
get() { return this.value; }
set(newVal) { this.$emit('input', newVal); }
}
}
}
</script>
You don't need to make a custom component for this case. You could simply use v-model.number in the parent and it would work. Once your inputs get more complex, you want to modify the set method a bit to set(newVal) { if (this.validateInput(newVal)) {this.$emit('input', newVal);} }, defining your own 'validateInput' method.
If you find you're writing a lot of different validations for different use cases, look into libraries like Vuelidate and VeeValidate

Prevent Vue Multiple Select to Store an Empty Array

I want this select multiple to pre-select one option, and not be able to deselect all options.
Whenever the last selected option is deselected it should be reselected. In other words when the user tries to deselect the last selected option it should visually not be deselected.
<template>
<b-select
if="Object.keys(doc).length !== 0 /* wait until firebase has loaded */"
:options="computedOptions"
v-model="model"
multiple
#input="onChange"
/>
</template>
<script>
//import Vue from 'vue'
import { fb } from "../fbconf";
export default {
name: "MyMultiSelect",
props: {
doc: Object, // firestore document
},
data() {
return {
options: []
};
},
firestore() {
var options = fb.db.collection("options");
return {
options: options
};
},
computed: {
computedOptions: function() {
return this.options.map(function(option) {
return {
text: option.name,
value: option.id
};
});
},
// to make sure mySelectedOptions is an array, before this.doc is loaded
// I use the following custom model
// because not using 'get' below causes a warning:
// [Vue warn]: <select multiple v-model="localValue"> expects an Array value for its binding, but got Undefined
model: {
get: function() {
if (!this.doc.hasOwnProperty('mySelectedOptions')) return []; // empty array before this.doc is loaded
else return this.doc['mySelectedOptions'];
},
set: function(newValue) {
// here I can prevent the empty array from being stored
// but visually the user can deselect all options, which is bad UX
//if (Array.isArray(newValue) && newValue.length > 0) this.doc['mySelectedOptions'] = newValue;
}
},
},
methods: {
onChange: function(newValue){
// I can manually store the array as I want here
// but I cannot in any way prevent the user from deselecting all options
if (Array.isArray(newValue) && newValue.length > 0) this.doc['mySelectedOptions'] = newValue;
else {
// none of these reselects the last selected option
var oldValue = this.doc['mySelectedOptions'];
this.doc['mySelectedOptions'] = this.doc['mySelectedOptions'];
//this.$forceUpdate();
//this.$emit("change", newValue);
//Vue.set(this.doc, 'mySelectedOptions', this.doc['mySelectedOptions']);
}
}
}
};
</script>
You could add watcher and when length becomes 0 just add previous value.
watch: {
model(val, oldVal) {
if(val.length == 0 && oldVal.length > 0) {
// take only one item in case there's clear button or etc.
this.model = [oldval[0]];
}
}
}

Need the same functionality as a computed property, but I need to be able to update the data after initial change

I have a situation where I need to update data when it detects changes to a state. The user needs to be able to make further changes this info within a textarea. Using computed properties pulls in the data exactly how I want, but any changes made by the user after this are overridden because the computed property keeps changing this data back to it's initial values. What would be the best way to pull in data initially upon a state change but then allow for editing after that point?
Thanks!
Edit: Updated to what i've tried for #Libby.
<textarea v-model="exampleData"></textarea>
computed: {
...mapGetters({
item: 'item'
})
methods: {
exampleFunction() {
this.exampleData = this.item;
}
mounted() {
this.exampleFunction();
}
Update exampleData in a watcher for item:
watch: {
item(value) {
this.exampleData = value;
}
}
This way you can bind your exampleData to the textfield, but changes to the item will still affect it.
And if you want exampleData to be initially set to the value of item, do that in the component's mounted hook:
mounted() {
this.exampleData = this.item;
}
Here's a fiddle.
If you set your property indata, you can initialize it in mounted which only runs once when the page is loaded:
data:
text: null
mounted: ->
text = "This text is initialized"
And then set v-model on your textarea
<textarea v-model="text"></textarea>
So the value of the textarea will start out as "This text is initialized", but the user will be able to change it, and those changes will be saved in text
Vue already has a built-in solution to handle this if you use the getter/setter syntax for computed properties
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
As a result, when your state changes you can update the computer property by assigning it a value:
// state has changed in text area handler
this.fullName = 'new value'