can't access "this" inside of component watcher nuxtjs - vue.js

I have a component with a state property called "volume" which is bound to a slider element. I have a watcher bound to the volume property such that when the volume is updated, a function should fire
data () {return {
volume: 0,
}},
methods: {
testMethod () {
console.log("this is firing")
}
},
watch: {
volume: (v) => {
console.log("volume has been updated", v);
this.testMethod();
}
}
On running this code, the console shows the error "Cannot read property of "testMethod" of undefined
I have tried other things like accessing the $store (which was my initial issue), and that is failing to resolve too.

You can't use the fat-arrow notation within a Vue.js component (Nuxt or otherwise). The fat-arrow function definition uses the wrong context (this in your case), which is why you are running into this problem.
<script>
export default {
data () {return {
volume: 0,
}},
methods: {
testMethod () {
console.log("this is firing")
}
},
watch: {
// Your old code.
// volume: (v) => {
// console.log("volume has been updated", v);
// this.testMethod();
// }
// The corrected way.
volume(v) {
console.log("volume has been updated", v);
this.testMethod();
}
}
};
</script>

You are using an Arrow Function, which binds the this keyword to the object that defines the function, rather than the object that called the function (which is the current instance of the component in this case).
Use regular function syntax instead:
watch: {
volume(v) {
console.log("volume has been updated", v);
this.testMethod();
}
}

Related

VueJS | Method "watch" has type "object" in the component definition

Currently I have following Watches in Product.vue file
watch: {
isOnline: {
async handler (isOnline) {
if (isOnline) {
const maxQuantity = await this.getQuantity();
this.maxQuantity = maxQuantity;
}
}
},
isMicrocartOpen: {
async handler (isOpen) {
if (isOpen) {
const maxQuantity = await this.getQuantity();
this.maxQuantity = maxQuantity;
}
},
immediate: true
},
isSample (curr, old) {
if (curr !== old) {
if (!curr) {
console.log('send the updateCall', curr);
// this.updateProductQty(this.product.qty);
pullCartSync(this);
}
}
}
}
but I am getting this following error (Vue Warn) in console
[Vue warn]: Method "watch" has type "object" in the component definition. Did you reference the function correctly?
I'm not sure why i am getting this error, as the syntax i am using seems to be correct, and its even functioning right.
Any suggestions why its giving this warning in error console?
Update:
Location where i have used the watch in vue page.
You have something like methods: { watch: {} } in your component definition. That's why vue is complaining. That might be added by a mixin as well.

How to compute a property based on an object with fallback

I have a component that receives an object as prop, like this:
props: ['propObject']
Then, there's a default object defined (I use VueX, so it's actually defined as a $store getter, but to make it simpler, let's say it's defined in the data method) in the data:
data() {
return {
dataObject: {defaultValueA: 1, defaultValueB: 2}
}
}
And I'd like to have a computed property that would behavior like this:
computed: {
computedObject() {
return Object.values(this.propObject).length > 0 ? this.propObject : this.dataObject;
}
}
However, I know this is not possible because Vue watchers don't watch for changes in the key/value pairs of an object.
I have tried to go with a watched property, like this:
props: ['propObject'],
data() {
return {
object: {},
defaultObject: {}
}
},
watch: {
propObject: {
handler: function() {
this.setComputedObject();
},
deep: true
}
},
methods: {
setComputedObject() {
this.object = Object.values(this.propObject).length > 0 ? this.propObject : this.defaultObject;
}
},
mounted() {
this.setComputedObject();
}
However, the watcher handler is not being called at all when the propObject changes, but if I call it directly via console, it works. Is there any way that I can make the computedObject become reactive?
you need to use Vue.set/vm.$set where you change the props (in source component)
for example
changeProp(){
this.$set(propObject,'newprop','newval');
}
and then just you regualr compouted in the target component (the component which receive the prop)
source : https://v2.vuejs.org/v2/guide/list.html#Object-Change-Detection-Caveats

VueJS: variable is undefined inside computed only

I'm trying to make async autocomplete input with Vue, Nuxt, Axios and Buefy. It basically works, but I need to have different strings when user just starts typing and there's yet nothing to show, and when there is nothing found for such request.
I'm checking in computed variable if input value isn't empty and axios returns empty array to handle if the request address cannot be found. But it causes error
Cannot read property 'length' of undefined
The weird thing is that address variable is successfully used in other parts of my component.
My vue file below:
<template lang="pug">
b-field(label="Your address?")
b-autocomplete(
rounded,
v-model="address",
:data="data",
placeholder="Start typing",
icon="magnify",
#input="getAsyncData",
#select="option => selected = option",
:loading="isFetching"
)
template(slot="empty") {{ dummyText }}
</template>
<script>
import axios from 'axios'
import debounce from 'lodash/debounce'
export default {
data() {
return {
data: [],
address: '',
selected: null,
isFetching: false,
nothingFound: false,
test: false
}
},
computed: {
dummyText: () => {
if (this.address.length > 0 && this.nothingFound) { // This will return error
return 'There is no such address'
} else {
return 'Keep typing'
}
}
},
methods: {
getAsyncData: debounce(function () {
this.isFetching = true
axios.post('https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address', {
"query": this.address,
"count": 8
}, {
headers: {
'Authorization': 'Token sometoken',
'Content-Type': 'application/json',
'Accept': 'application/json',
}
})
.then(response => {
this.isFetching = false
this.data = Object.values(response.data.suggestions)
if (response.data.suggestions.length===0) this.nothingFound = true
console.log(this.address.length) // This will work
})
.catch(error => {
this.isFetching = false
console.log(error);
})
}, 300)
}
}
</script>
This is not about ssr, I've tried to init component inside mounted hook. Think I'm missing out something obvious, but I've already spent hours trying to fix this without success
Don't use arrow function ()=>{} for computed, it will cause the wrong context (not current Vue instance).
Change to function () {} then it should work fine.
And for methods, watch, you should follow same rules.
computed: {
dummyText: function () { // change to function () {}
if (this.address.length > 0 && this.nothingFound) { // This will return error
return 'There is no such address'
} else {
return 'Keep typing'
}
}
},
You can also use es2015 shorthand for a method function:
computed: {
dummyText() {
return this.address.length > 0 && this.nothingFound ? 'There is no such address' : 'Keep typing';
}
}
The Vue Documentation states not to use arrow functions on a property or callback.
You are facing this error because an arrow function wouldn't bind this to the vue instance for which you are defining the computed property as arrow functions are bound to the parent context and this.address is undefined. Same would happen if you use arrow function for methods.
Use regular function:
dummyText: function () {
console.log(this.address)
}
Or use ES5 shorthand:
dummyText() {
console.log(this.address)
}
Or If you want to keep using the arrow function, you could pass the component instance (this) as parameter because computed properties receive component instance as their first argument. :
dummyText : ctx => console.log(ctx.address)

vuejs2: how can i destroy a watcher?

How can i destroy this watcher? I need it only one time in my child component, when my async data has loaded from the parent component.
export default {
...
watch: {
data: function(){
this.sortBy();
},
},
...
}
gregor ;)
If you construct a watcher dynamically by calling vm.$watch function, it returns a function that may be called at a later point in time to disable (remove) that particular watcher.
Don't put the watcher statically in the component, as in your code, but do something like:
created() {
var unwatch = this.$watch(....)
// now the watcher is watching and you can disable it
// by calling unwatch() somewhere else;
// you can store the unwatch function to a variable in the data
// or whatever suits you best
}
More thorough explanation may be found from here: https://codingexplained.com/coding/front-end/vue-js/adding-removing-watchers-dynamically
Here is an example:
<script>
export default {
data() {
return {
employee: {
teams: []
},
employeeTeamsWatcher: null,
};
},
created() {
this.employeeTeamsWatcher = this.$watch('employee.teams', (newVal, oldVal) => {
this.setActiveTeamTabName();
});
},
methods: {
setActiveTeamTabName() {
if (this.employee.teams.length) {
// once you got your desired condition satisfied then unwatch by calling:
this.employeeTeamsWatcher();
}
},
},
};
</script>
If you are using vue2 using the composition-api plugin or vue3, you can use WatchStopHandle which is returned by watch e.g.:
const x = ref(0);
setInterval(() => {
x.value++;
}, 1000);
const unwatch = watch(
() => x.value,
() => {
console.log(x.value);
x.value++;
// stop watch:
if (x.value > 3) unwatch();
}
);
For this kind of stuff, you can investigate the type declaration of the API, which is very helpful, just hover the mouse on it, and it will show you a hint about what you can do:

How to access nested functions in Vue.js?

Say, we have a Vue.js component built this way:
export default {
watch: {
// vars
}
},
methods: {
functionOne: function() {
function nestedFuncOne(p, q) {
// doing something
}
},
functionTwo: function() {
function nestedFuncTwo(r, s) {
// doing something
}
nestedFuncOne(param1, param2); // how to call it?
}
},
(...)
How should an inner function from the first method be called from inside of second method? this seems not to give desired output here.