I have currently assigned a setTimeout function to a Vue method and I want to use clearTimeout for this function. Is that possible? If so, how can I do this?
methods: {
timeoutController() {
setTimeout(function() {
this.controllerShown = false;
}, 3000);
}
....
new Vue({
el: '#app',
data: {
timer: null
},
methods: {
startTimer () {
this.timer = setTimeout(() => {
console.log("execute me")
}, 3000)
},
// If you kill the timer before setTimeout callback has been executed the callback wont get executed
killTimer () {
if (this.timer) {
clearTimeout(this.timer)
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="startTimer()">start</button>
<button #click="killTimer()">kill</button>
</div>
Why do you need to assign a function to a Vue component's data?
setTimeout() will execute the function 3000 ms later and give the timer to this.timeout. The timer will never execute again, no matter where you assign to.
Related
I have a v-textarea, I write something to area and if I do not any action, in v-textarea context must be cleared automatic after a certain time (2 minutes). How can I do it?
<v-textarea
v-model.trim="text"
clearable
:label="modeLabel"
>
I would do something like this with a timer.
<v-textarea
v-model.trim="text"
clearable
:label="modeLabel"
#change="clearHandler(event)"
>
var timerID = null;
function clearHandler() {
if (timerID) {
clearTimeout(timerID);
}
// create a timer to clear the textarea by setting the model to empty string
timerID = setTimeout(() => {
this.text = "";
}, 120000);
}
Also, I would use this.timerID or a ref variable, instead of var like my example, depending on what version of vue you are using.
I've created this with a simple watcher. First, we create a function that includes the timeout. After that we create a watcher triggered to val. In this watcher, we call the timeout and after that clear the timeout. The reason for this is that we want it to re-run the function. The watcher also watches the value, if the user enters sth, the timeout will be canceled.
new Vue({
el: "#app",
data() {
return {
val: ''
}
},
methods: {
timeout() {
return setTimeout(() => {
this.val = ''
}, 5000)
}
},
watch: {
val: {
handler() {
this.timeout()
clearTimeout(this.timeout);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="val" />
</div>
I have a child component that emits a value, and in the parent I perform an axios call with this value each time it is emitted. My problem is that I want to trigger the axios call only if in x ms (or seconds) the child has not emmited another value in order to reduce the amount of calls I do.
Code here :
<script>
import axios from "axios";
import DataTable from './DataTable.vue';
export default {
name: 'Test',
data() {
return {
gamertags: [],
// Utils
timeout: 500,
delay: 500
}
},
methods: {
// API calls
async getGamerTags(string='') {
const path = `http://localhost:5000/gamertags?string=${string}`
await axios.get(path)
.then((res) => {
this.gamertags = res.data;
})
.catch((error) => {
console.error(error);
});
},
// DataTable
handleFilters(filters) {
clearTimeout(this.timeout);
this.timeout = setTimeout(this.getGamerTags(filters.find(o => o.field == "playerGamerTag").filter), this.delay);
}
}
components: {
DataTable
}
};
</script>
<template>
<DataTable
#filters="handleFilters"
/>
</template>
Thanks in advance.
What you need is debouncing. Here is an example:
var timeout, delay = 3000;
function func1() {
clearTimeout(timeout);
timeout = setTimeout(function(){
alert("3000 ms inactivity");
}, delay);
}
<input type="text" oninput="func1()">
When emitted, simply call func1(), and if there are no new emissions after 3000 ms, the function in timeout will be executed.
It would be better to understand the problem and use case if you add the code also.
but As I could understand the problem these is two way
if you using inside input and triggering based #changed event you can add #change.lazy this not trigger on each change.
second solution is to use setTimeout(function,delayInMs) inside parent
vuejs Docs link
By simply changing the handleFilters function to :
handleFilters(filters) {
clearTimeout(this.timeout);
this.timeout = setTimeout(
this.getGamerTags,
this.delay,
filters.find(o => o.field == "playerGamerTag").filter
);
},
the problem is solved.
form(#submit.prevent="onSubmit")
input(type="text" v-model="platform" placeholder="Add platform name...")
input(type="submit" value="Submit" class="button" #click="clicked = true")
button(type="button" value="Cancel" class="btn" #click="cancelNew") Cancel
h3(v-if="clicked") Thank you for adding a new platform
span {{ countdown }}
This is my template and when the user submits the form, I want to count down from 3 using setTimeout function and submit after 3 seconds.
If I have it this way, it works;
data() {
return {
countdown: 3,
platform: ""
}
},
methods: {
countDownTimer() {
setTimeout(() => {
this.countdown -= 1
this.countDownTimer()
}, 1000)
},
onSubmit() {
let newplatform = {
name: this.platform
}
this.addPlatform(newplatform)
this.platform = ' '
this.countDownTimer()
}
}
However I have 3 more forms and I didn't want to repeat the code. So I wanted to put countdown in the store,
countDownTimer({commit}) {
setTimeout(() => {
countdown = state.countdown
countdown -= 1
commit('COUNTDOWN', countdown)
this.countDownTimer()
}, 1000)
}
and mutate it like
COUNTDOWN(state, countdown) {
state.countdown = countdown
}
This doesn't work and I am not sure If I am able to change the state, commit the changes inside of settimeout function? Is there a better way I can implement this?
The issues:
The recursive setTimeout isn't stopped.
The countdown timer isn't reset.
Use setInterval (and clearInterval) instead of the recursive setTimeout.
For async logic including setTimeout, use an action rather than a mutation.
Include state from the context object (where you get commit), or it will be undefined.
Try this:
actions: {
countDownTimer({ state, commit, dispatch }) { // state, commit, dispatch
commit('RESET');
const interval = setInterval(() => { // Use `setInterval` and store it
commit('COUNTDOWN');
if (state.countdown === 0) {
clearInterval(interval); // Clear the interval
dispatch('updateDatabase'); // Call another action
}
}, 1000)
}
}
mutations: {
RESET(state) {
state.countdown = 3;
},
COUNTDOWN(state) {
state.countdown--;
}
}
I am using vue.js in this case but I guess it would apply in plain JS too. The problem is that when I am in a function that is in another function I am having to call variables by their full path like - Object.variable instead of this.variable. Is there a way to use this.timer, this.pages instead of TVComponent.pages etc.
const TVComponent = new Vue ({
el: '.tvContent',
data:
{
current_page: 0,
timer: 0,
pages: [
{ page: '/', interval: 10 },
{ page: 'tv/calls', interval: 10 },
{ page: 'tv/general', interval: 10 }
]
},
methods:
{
tvTimer()
{
setInterval(function() {
TVComponent.timer++;
if (TVComponent.timer == TVComponent.pages[TVComponent.current_page].interval) {
console.log('it is time!!');
}
console.log(TVComponent.pages[TVComponent.current_page].page);
}, 1000);
},
})
Classic problem of this being not what you expect in a function
A. bind it
methods:
{
tvTimer()
{
setInterval(function() {
// ...
}.bind(this), 1000);
},
})
B. use a closure
methods:
{
tvTimer()
const _this = this
{
setInterval(function() {
_this.timer ...
}, 1000);
},
})
C. use an arrow function
methods:
{
tvTimer()
{
setInterval(() => {
this.timer ...
}, 1000);
},
})
This is one of those things one has to really understand about JS in order to not fall for it over and over in different places. I suggest this ebook:
https://github.com/getify/You-Dont-Know-JS/blob/master/up%20%26%20going/ch2.md#this-identifier
The function you pass to setInterval receives its own context, however if you use an arrow function it will use the current context instead, and ignore the one given by setInterval.
setInterval(() => { ... }, 1000)
This is because setInterval() this object is not same as vue.js this, since they have different scopes.
Try to assign this object to new variable before entering the problematic function's scope.
let self = this;
tvTimer()
{
setInterval(function() {
self.timer++;
if (self.timer == self.pages[self.current_page].interval) {
console.log('it is time!!');
}
console.log(self.pages[self.current_page].page);
}, 1000);
},
See: https://developer.mozilla.org/en-US/docs/Glossary/Scope
I think you have to bind it in this context. We do it this way in React classess.
code:
myMethod = () => {
setTimeout(()=> {
if(!this.state.otherFuncHasBeenCalled) {
this.myMethod()
}
},5000)
}
so the otherFuncHasBeenCalled state is set to true if my other function is called. but what I wanted to do here is when myMethod is invoked, after 5 seconds, if my other function is NOT called that sets otherFuncHasBeenCalled state to true, invoke myMethod again. But it re renders the component multiple times. Help?
How about something like this?
myMethod = () => {
setTimeout(()=> {
if(!this.state.otherFuncHasBeenCalled) { // wait 5 sec for check
this.setState(
{otherFuncHasBeenCalled: true}, // update state
this.myMethod // run again after state has been set
}
},5000)
}