watch not update change vue - vue.js

i'm try to change variable by watch and change it in to html
<p>{{customer.creditsLeft}}</p>
and vue
data() {
customer: {},
}
watch: {
'$store.state.jobs.listBooking.customer': function (newVal) {
this.customer.creditsLeft = newVal;
console.log('current credit now' + this.customer.creditsLeft);
return this.customer.creditsLeft;
}
},
console.log is woking but creditsLeft still not change. i'm a new bie in vue . pls help me

If you want to add new property to customer object you need to use set, otherwise it's not reactive.
this.$set(this.customer, 'creditsLeft', newVal)
https://v2.vuejs.org/v2/guide/reactivity.html
Or you can set it before hand so you don't need to use set
data() {
customer: {
creditsLeft: 0
},
}

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 to locally store a computed variable property once in data

I have a computed variable getting a value from the VUEX store. I only want to get this value once, and then save it locally into a variable so I can edit the said variable without doing any mutations on my store value. How can this be done?
Any help will be really appreciated.
What you can do is create a computed property with a get and set method:
data() {
return {
myValueCopy: null
}
},
computed: {
myValue: {
get() {
if (this.myValueCopy === null) {
return this.$store.getters.myValue;
}
return this.myValueCopy;
},
set(value) {
this.myValueCopy = value;
}
}
}
If there's no local copy of the data return the store value, else return the copy. When setting the data, update the local copy not the store.
I ended up using something similar to the other answer:
data() {
return {
LocalText: { type: String },
}
},
computed: {
...mapGetters('store', ['storevar']),
currentText: function () {
return this.storevar
},
},
created() {
this.LocalText= this.currentText
}
What I have done here is that I first made a new variable for the text to be locally stored - LocalText. I have also added a getter that gets the store variable from the store and a computed function that returns the value of the storevar. Finally, I have assigned the value of the storevar to my LocalText when the page is first created using the created() function.

Vuejs: listen to props changes and use it

I am developing a project using Vue JS and I need to watch the props changes and call it inside a <span>.
I have used watch() and it shows that the props values are assigned.
But when I call it inside the <span> the value is not showing.
props: ['verifyText', 'verifyValue', 'profileId', 'logged', 'verifyType', 'status'],
watch: {
verifyText: function () { // watch it
this.verify_text = this.verifyText;
},
verifyValue: function () {
this.verify_value = this.verifyValue;
},
verifyType: function () {
this.verify_type = this.verifyType;
}
},
data() {
return {
verify_type: this.verifyType,
verify_text: this.verifyText,
verify_value: this.verifyValue,
}
},
//using inside span
<span>{{verify_text}}</span>
Receive and insert new data that changes from 'watch'
Try this.
props: ['verifyText', 'verifyValue', 'profileId', 'logged', 'verifyType', 'status'],
watch: {
verifyText: function (new_value) {
this.verify_text = new_value;
}
},
data() {
return {
verify_text: this.verifyText,
}
},
//using inside span
<span>{{verify_text}}</span>
I solved this issue by watching the verify_text in the parent component.
'verify_text': function (value) {
this.verify_text = value;
},
Same for the verify_type and verify_value
Thank you all for replying.

Access component instance from vue watcher

I'm working on a project, similar as a bill manager, so I want that the subtotal get recalculated every time that quantity or unit value change, I have tried and searched to accomplish this using watcher or computed properties, but I don't find the right approach, cause I need to access the whole scope of the element when another change, like this.
Model structure:
detail
quantity
unit value
subtotal (should be a computed or updated)
So I think I should be able of doing something like this:
Vue.component('item', {
template: '#item',
props: {
item: Object,
},
computed:{
total: function(){
return this.quantity*this.unit_value;
}
},
watch:{
'item.quantity':()=>{
this.subtotal = this.quantity*this.unit_value;
}
}
});
I have several components being read from a list
I merged the approach using watcher and computed in the same code to make it shorter.
The problem is that I haven't found a way to access the hole element from inside itself, anyone could pls explain the right way? thanks
You shouldn't use arrows functions there, use method declarations.
If you want to watch for a property of the item object, you'll have to watch for the item object itself, and additionally use the deep: true flag of the watcher.
Final detail, you are using several properties that are not declared in your data. Declare them, otherwise they will not be reactive, that is, the computed will not recalculate when they change.
See code:
Vue.component('item', {
template: '#item',
props: {
item: Object,
},
data() {
return {
subtotal: null, // added data properties
quantity: null,
unit_value: null
}
},
computed: {
total: function() {
return this.quantity * this.unit_value;
}
},
watch: {
item: { // watching for item now
deep: true, // using deep: true
handler() { // and NOT using arrow functions
this.subtotal = this.quantity * this.unit_value;
}
}
}
});

Using $refs in a computed property

How do I access $refs inside computed? It's always undefined the first time the computed property is run.
Going to answer my own question here, I couldn't find a satisfactory answer anywhere else. Sometimes you just need access to a dom element to make some calculations. Hopefully this is helpful to others.
I had to trick Vue to update the computed property once the component was mounted.
Vue.component('my-component', {
data(){
return {
isMounted: false
}
},
computed:{
property(){
if(!this.isMounted)
return;
// this.$refs is available
}
},
mounted(){
this.isMounted = true;
}
})
I think it is important to quote the Vue js guide:
$refs are only populated after the component has been rendered, and they are not reactive. It is only meant as an escape hatch for direct child manipulation - you should avoid accessing $refs from within templates or computed properties.
It is therefore not something you're supposed to do, although you can always hack your way around it.
If you need the $refs after an v-if you could use the updated() hook.
<div v-if="myProp"></div>
updated() {
if (!this.myProp) return;
/// this.$refs is available
},
I just came with this same problem and realized that this is the type of situation that computed properties will not work.
According to the current documentation (https://v2.vuejs.org/v2/guide/computed.html):
"[...]Instead of a computed property, we can define the same function as a method. For the end result, the two approaches are indeed exactly the same. However, the difference is that computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when some of its reactive dependencies have changed"
So, what (probably) happen in these situations is that finishing the mounted lifecycle of the component and setting the refs doesn't count as a reactive change on the dependencies of the computed property.
For example, in my case I have a button that need to be disabled when there is no selected row in my ref table.
So, this code will not work:
<button :disabled="!anySelected">Test</button>
computed: {
anySelected () {
if (!this.$refs.table) return false
return this.$refs.table.selected.length > 0
}
}
What you can do is replace the computed property to a method, and that should work properly:
<button :disabled="!anySelected()">Test</button>
methods: {
anySelected () {
if (!this.$refs.table) return false
return this.$refs.table.selected.length > 0
}
}
For others users like me that need just pass some data to prop, I used data instead of computed
Vue.component('my-component', {
data(){
return {
myProp: null
}
},
mounted(){
this.myProp= 'hello'
//$refs is available
// this.myProp is reactive, bind will work to property
}
})
Use property binding if you want. :disabled prop is reactive in this case
<button :disabled="$refs.email ? $refs.email.$v.$invalid : true">Login</button>
But to check two fields i found no other way as dummy method:
<button :disabled="$refs.password ? checkIsValid($refs.email.$v.$invalid, $refs.password.$v.$invalid) : true">
{{data.submitButton.value}}
</button>
methods: {
checkIsValid(email, password) {
return email || password;
}
}
I was in a similar situation and I fixed it with:
data: () => {
return {
foo: null,
}, // data
And then you watch the variable:
watch: {
foo: function() {
if(this.$refs)
this.myVideo = this.$refs.webcam.$el;
return null;
},
} // watch
Notice the if that evaluates the existence of this.$refs and when it changes you get your data.
What I did is to store the references into a data property. Then, I populate this data attribute in mounted event.
data() {
return {
childComps: [] // reference to child comps
}
},
methods: {
// method to populate the data array
getChildComponent() {
var listComps = [];
if (this.$refs && this.$refs.childComps) {
this.$refs.childComps.forEach(comp => {
listComps.push(comp);
});
}
return this.childComps = listComps;
}
},
mounted() {
// Populates only when it is mounted
this.getChildComponent();
},
computed: {
propBasedOnComps() {
var total = 0;
// reference not to $refs but to data childComps array
this.childComps.forEach(comp => {
total += comp.compPropOrMethod;
});
return total;
}
}
Another approach is to avoid $refs completely and just subscribe to events from the child component.
It requires an explicit setter in the child component, but it is reactive and not dependent on mount timing.
Parent component:
<script>
{
data() {
return {
childFoo: null,
}
}
}
</script>
<template>
<div>
<Child #foo="childFoo = $event" />
<!-- reacts to the child foo property -->
{{ childFoo }}
</div>
</template>
Child component:
{
data() {
const data = {
foo: null,
}
this.$emit('foo', data)
return data
},
emits: ['foo'],
methods: {
setFoo(foo) {
this.foo = foo
this.$emit('foo', foo)
}
}
}
<!-- template that calls setFoo e.g. on click -->