The this is undefined if used inside a debounce function - javascript

For some reason the this is undefined if I'm using a debounce function. I have tried to bind it but still it is undefined. I can't understand what is happening here..
For example here this is working and returns
VueComponent {_uid: 6, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
<template>
<at-ta #at="fetchMembers">
<textarea></textarea>
</at-ta>
</template>
fetchMembers(at)
{
console.log(this) // <-- VueComponent { ...
},
But when I move it to a debounce function it isn't working anymore but is
this is undefined
fetchMembers: debounce(at => {
axios.get(`/api/users?name=${at}`).then(({data}) => {
this.members = data // <-- this is undefined
})
}, 500)

Don't bind the debounced function, but do bind the axios promise callback.
methods: {
fetchMembers: debounce(function (at) {
axios.get(`/api/users?name=${at}`).then(({data}) => {
this.members = data
})
}, 500)
}
It didn't work for you because you used a fat arrow function for the debounced function where this is undefined at that scope.
Keep in mind that you're only creating one debounced function that will be shared across all instances of your component. If this is an issue, you can instead wrap fetchMembers with a debounced function in the created hook:
created() {
this.fetchMembers = _.debounce(this.fetchMembers, 500)
},
methods: {
fetchMembers() {
axios.get(`/api/users?name=${at}`).then(({data}) => {
this.members = data
})
}
},

Related

Vuejs watching an object then execute a method fails

I want to watch a prop which is an object so i have
<script>
export default {
watch:{
filter: {
handler:(newval)=> {
console.log("i have new data",newval) //this works
this.fetchData(); //throws an error
},
deep: true
}
},
props:{
filter:{
type:Object,
required:true
}
},
data: () => ({
pagination: {},
items: []
}),
methods:{
fetchData(){
console.log("am fetching the data");
}
}
}
The above watcher works as the console.log displays the new value but i cannot execute a method as on the watch am getting an error Error in callback for watcher "filter": "TypeError: _this.fetchData is not a function". How can i execute a method on a deep watcher.
Move Arrow function to simple function for handler method. Change handler:(newval)=> { to handler: function (newval) {:
Vue.js docs:
Don’t use arrow functions on an options property or callback, such as created: () => console.log(this.a) or vm.$watch('a', newValue => this.myMethod()).
handler: function (newval) {
console.log("i have new data",newval) //this works
this.fetchData(); // it should work
},

Uncaught (in promise) TypeError: Cannot set property 'playerName' of undefined at eval

I'm trying to assign response.data.Response.displayName from my GET request to my playerName property, however, I am getting an error "Uncaught (in promise) TypeError: Cannot set property 'playerName' of undefined at eval". I am successfully console logging response.data.Reponse.displayName so there is a displayName in it.
Why am I getting this error?
export default {
data: function() {
return {
playerName: ''
}
},
methods: {
},
mounted() {
axios.get('/User/GetBungieNetUserById/19964531/')
.then(function(response) {
this.playerName = response.data.Response.displayName
console.log(response.data.Response.displayName)
});
}
}
Other comments and answers are correct - using an arrow/lambda function instead of just function will work. But there's a nuance as to why.
Javascript's concept of this is well defined but not always what you'd expect from other languages. this can change within one scope block when you're executing from sub-functions of things like callbacks. In your case, the function in the then no longer understands this as the same as if you were running the same code directly inside mounted().
You can bind functions, however, to (among other purposes) have a specific this attached that can't be changed. Arrow functions do this implicitly, and bind this to what this is in the context the arrow function is created. Therefore, this code:
axios.get('/User/GetBungieNetUserById/19964531/')
.then((response) => {
this.playerName = response.data.Response.displayName
console.log(response.data.Response.displayName)
});
understands this properly. It is (roughly!) equivalent to the following:
axios.get('/User/GetBungieNetUserById/19964531/')
.then((function(response) {
this.playerName = response.data.Response.displayName
console.log(response.data.Response.displayName)
}).bind(this));
Use lambda function ( Arrow function ) to reach the code
export default {
data: function() {
return {
playerName: ''
}
},
methods: {
},
mounted() {
axios.get('/User/GetBungieNetUserById/19964531/')
.then((response) => {
self.playerName = response.data.Response.displayName
console.log(response.data.Response.displayName)
});
}
}

How to use data from one hook to other hook in Vue.js?

In my vue.js application I send request by axios package in created() hook. I add response to array called coordinates. I want to use that array outside of created() hook. For example in mounted() hook or in functions which we can set in methods.
Right now when I tried to use self.coordinates outside created() hook it return undefined. When I use this.coordinates it return just [__ob__: Observer].
Whats wrong I did?
export default {
name: "Map",
data() {
return {
coordinates: [],
}
},
created() {
let self = this;
axios.get('URL').then(function (response) {
let coordinates = [];
for (let i = 0; i < response.data.length; i++) {
coordinates.push([response.data[i]["LATITUDE"], response.data[i]["LONGITUDE"]]);
}
self.coordinates = coordinates;
});
},
mounted() {
console.log(self.coordinates); // undefined
consol.log(this.coordinates); // [__ob__: Observer]
},
}
I would prefer "mounted" and move the logic into methods for reusability. The method can be kicked from anywhere afterwards. In the example below, I prefered kicking the method direcly. Watchers is another option.
Here is the fiddle https://jsfiddle.net/dj79ux5t/2/
new Vue({
el: '#app',
data() {
return {
coordinates: []
}
},
mounted() {
let self = this;
axios.get('https://api.weather.gov/').then(function (response) {
self.coordinates = response.data;
self.greet();
});
},
methods: {
greet: function () {
console.warn(this.coordinates.status);
}
}
})
I think instead of mounted , you should use watch . You call some link so it will take time to load that data , watch method will trigger when your data is updated ...
watch: {
coordinates: {
handler: function (updateVal, oldVal) {
console.log(updateVal)
},
deep: true
}
},

Lodash's _.debounce() not working in Vue.js

I am trying to run a method called query() when a component property called q in Vue.js is modified.
This fails because this.query() is undefined. This is referring to my component's instance but somehow does not contain the methods.
Here's the relevant code part where I'm trying to watch the component property q and run the query() function:
methods: {
async query() {
const url = `https://example.com`;
const results = await axios({
url,
method: 'GET',
});
this.results = results.items;
},
debouncedQuery: _.debounce(() => { this.query(); }, 300),
},
watch: {
q() {
this.debouncedQuery();
},
},
Error:
TypeError: _this2.query is not a function
If I write the debounce() call as below, the TypeError: expected a function error appears even earlier, at the page load.
debouncedQuery: _.debounce(this.query, 300),
The issue comes from the lexical scope of the arrow function you define within _.debounce. this is bound to the object you are defining it in, not the instantiated Vue instance.
If you switch out your arrow function for a regular function the scope is bound correctly:
methods: {
// ...
debouncedQuery: _.debounce(function () { this.query(); }, 300)
}
We can do it by plain JS (ES6) with few lines of code:
function update() {
if(typeof window.LIT !== 'undefined') {
clearTimeout(window.LIT);
}
window.LIT = setTimeout(() => {
// do something...
}, 1000);
}
As answered in another post This is undefined in Vue, using debounce method the best way to add debouncing IMO is to create the method normally in methods as eg:
setHover() {
if (this.hoverStatus === 'entered') {
this.hoverStatus = 'active'
}
},
But then replace it in your created block eg:
created() {
this.setHover = debounce(this.setHover, 250)
},

Testing a method of an object returned by a function

I have a function that returns an object with many methods and I need to check one of the methods inside this returned object. I am using AngularJS and Karma+Jasmine as testing suite. How do I call methods inside the object returned by a function?
function modalOptions() {
.........
return this.$q((resolve) => {
// test accessable here
this.solveModel = {
save: () => {
// test can't call save()
this.saveToDB = this.toSendToDB;
},
cancel: () => { ...
},
delete: () => { ...
}
};
});
}
My test is somewhat like this...
it('should save modal with the data', function() {
scope.$apply();
expect(vm.modalOptions).toBeDefined();
vm.toSendToDB = true; // hard-coded
vm.savedToDB = undefined // default value from other controller
spyOn(vm, 'modalOptions').and.callThrough();
console.log(vm.modalOptions()); // gives weird response: c{$$state: Object{status: 0}} instead of the solveModal object
expect(vm.toSendToDB).toBeTruthy();
expect(vm.savedToDB).toBeTruthy();
});
Sorry, I can not comment yet, but the promise has to be resolved and the solveModel passed to it, in order for solveModel to be returned. Where do you resolve the promise?

Categories

Resources