Vuejs - On input, run a function (but with a delay) - javascript

I have an input field, and v-on:input it runs a method called activate that looks like this:
export default: {
data() {
return {
isHidden: true
}
},
methods: {
activate() {
this.isHidden = false;
}
}
}
isHidden turns on/off some icon (it doesn't really matter what this data property is; I'm just using it for example purposes).
So currently, when a user does an input it immediately turns on the activate function. Is there a way to, perhaps, put it on a delay via setTimeout? I've tried doing the following but it doesn't work:
methods: {
setTimeout(function() {
activate() {
this.isHidden = false;
}
}, 500)
}

Try this:
methods: {
activate() {
setTimeout(() => this.isHidden = false, 500);
}
}

Or without arrow function:
methods: {
activate() {
var that = this;
setTimeout(function() { that.isHidden = false; }, 500);
}
}

First, set a var in your data:
data() {
return {
typing: Date.now()
}
}
then in your methods, create a function that will fire on keyup:
pendingSave(val){
let saving = setTimeout(() => {
this.saveItem(val) // method to call when user is done typing
},1203)
if(val){
saving
if(Date.now() - this.typing < 1200) clearTimeout(saving)
this.typing = Date.now();
}
}
In your HTML, you would have something like this:
<input v-model="title" v-on:keyup="pendingSave(title)" type="text" placeholder="Title" />
What happens is that when the user clicks inside the input, and types a character, it will call 'pendingSave()'. This will get the function ready to call via setTimeout() in 1203ms. If the user types another character, and the time is 'less' than 1200ms, it will cancel setTimeout()... otherwise it will fire the function saveItem() as intended.

Related

How to make .one () method to work again without reloading the page

I have a chat application and I made that when the user writes in the TEXTAREA field to add a text under his name for example Typing ... but for personal reasons I would like this "Typing ..." to appear only once without repeating for each character.
I tried with the one () function but it works again only if user reloads the page.
$("textarea").one('input', function () {
HERE IS MY CODE TO ADD "TYPING.." UNDER HIS NAME
});
function sendMessage() {
HERE IS MY CODE TO DELETE "TYPING..." FROM UNDER HIS NAME
}
How can I make it work?
You could use a kind of throttling, using the following setTimeout-based, function:
// Returns a function that will call its callback argument
// only when a certain delay has passed. Another callback
// can be called to notify that the delay has expired
function throttle(f, milliseconds, ready = () => null) {
let timer = -1;
return function () {
if (timer === -1) f();
clearTimeout(timer);
timer = setTimeout(function () {
timer = -1;
ready();
}, milliseconds);
}
}
function sendMessage(msg) {
$("div").text("typing...");
}
function clearMessage() {
$("div").text("");
}
$("textarea").on('input', throttle(sendMessage, 3000, clearMessage));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea></textarea>
<div></div>
The "typing..." message will clear when during 3 seconds there was no typing. If typing starts again, then the message will be sent/displayed again. The message will not be sent again before it has been cleared.
You could work with a timeout, that will revert the typing state after a certain time. Clear the timeout while the user keeps typing.
const textArea = document.querySelector('.area')
const indicator = document.querySelector('.indicator')
let timeout = null
textArea.addEventListener('input', function() {
clearTimeout(timeout)
indicator.innerText = 'Typing...'
timeout = setTimeout(function() {
indicator.innerText = ''
}, 300)
})
.area,
.indicator {
display: block;
margin: 1rem;
}
<textarea class="area"></textarea>
<span class="indicator"></span>

Debounce function does not work when directly not bound to a button

I am trying to use Ben Alman's debounce code. I have the following code but I don't see anything executing at all.
onChange() {
var this_ = this
if (this.autoRefreshOn) {
Cowboy.debounce(function(e) {
console.log("debounce worked");
this_.doSomethingFunction();
}, 250);
}
}
This onChange() function is fired from multiselect boxes like this:
<ss-multiselect-dropdown (ngModelChange)="onChange($event)"></ss-multiselect-dropdown>
<ss-multiselect-dropdown (ngModelChange)="onChange($event)"></ss-multiselect-dropdown>
<ss-multiselect-dropdown (ngModelChange)="onChange($event)"></ss-multiselect-dropdown>
<ss-multiselect-dropdown (ngModelChange)="onChange($event)"></ss-multiselect-dropdown>
When these select boxes are checked they continuously fire onChange() but I do not see any executions from the debounce function.
All of the examples I found on the web implement a debounce function that is bind to a button press or something like that.
You can add a debounce directly to your onChange() method and call the newly created debounced method directly in your template:
component.ts
limitedOnChange = this.debounce(this.onChange, 250);
onChange(event) { console.log('change detected: ', event) }
debounce(fn, delay) {
let timer = null;
return function () {
const context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
component.html
<ss-multiselect-dropdown (ngModelChange)="limitedOnChange($event)"></ss-multiselect-dropdown>

Set time out event on a m() generated element

I have a m("p.help") element which is removed with a click event.
I also want the element to be removed automatically after a few seconds if not clicked. I need to set a time out on it. Setting time out does not work.
function help() {
var text = `This is a service template. Use Service section to set the schedule`;
var node = m("p.help", {
onclick() {
this.parentNode.removeChild(this);
},
}, text);
setTimeout(() => {
if (node.parentNode) {
node.parentNode.removeChild(node);
console.log("removed");
m.redraw();
}
}, 5000);
return node;
}
The click event works fine but the time out does not work. It is not even triggered judging by the console.log()
What am I doing wrong?
EDIT
Thanks ciscoheat for the tip.
I had to put the timer in the controller for this to work.
So this one works fine:
function controller(init) {
this.display = {
help: true
};
setTimeout(() => {
this.display.help = false;
m.redraw();
}, 5000);
}
function view(vm) {
return m(".container", [
(() => {
var text = "Some text";
if (vm.display.help) {
return m("p.memo", {
onclick() {
this.parentNode.removeChild(this);
}
}, text);
}
})(),
]);
}
To use Mithril correctly, you should avoid DOM manipulation, leaving that to Mithril's fast diff algorithm.
Use a state variable instead, related to displaying the help paragraph that will be changed automatically after 5 seconds.
Here's a jsbin showing what I mean: http://jsbin.com/kixece/edit?html,js,output

How to stop calling a delayed function if some other event happens in that period?

After a certain event happens, I have this function (rightMenu) that triggers with 1.5s delay. However, my challenge is to figure out how to check if the leftMenu is called during this period then stop the setTimeout function to call the rightMenu.
var leftMenu = function() {
// some code here
}
var rightMenu = function() {
// some code here
}
$('#leftMenu').on('click', function() {
leftMenu();
})
$('#rightMenu').on('click', function() {
rightMenu();
})
if (...) {
leftMenu();
} else {
setTimeout(function() {
rightMenu();
}, 1500);
}
I'm not 100% sure if this is what you're asking, but you can clear intervals. For example:
function asyncFun() {
// some code
}
setTimeout(asyncFun, 5000); // this will run after 5 seconds
var asyncHandle = setTimeout(asyncFun, 10000);
clearTimeout(asyncHandle); // this cancels the function call
I feel like this is what you're asking...
If not, the other interpretation that I have is you want to temporarily remove the event handler from the #leftmenu and #rightmenu when you're in the other menu. To do this, you can clear event handlers in jQuery with $("#rightmenu").off("click"). This function is basically the opposite of .on. See here. Good luck!
Yet another possible fix to your code:
/*
initialize your variable here. Technically doesn't change anything
because of hoisting, but I'm guessing based on your question you
haven't learned that yet.
*/
var callingRight;
var leftMenu = function() {
// some code here
}
var rightMenu = function() {
// some code here
}
$('#leftMenu').on('click', function() {
clearTimeout(callingRight); // clear the timeout on your global variable here.
leftMenu();
})
$('#rightMenu').on('click', function() {
rightMenu();
})
if (...) {
leftMenu();
} else {
callingRight = setTimeout(function() { // assign this setTimeout to your global variable
rightMenu();
}, 1500);
}

setInterval doesn't get cleared, function keeps getting executed

I have the following function:
function monitorClimate() {
var sensorReadingInterval;
function startClimateMonitoring(interval) {
sensorReadingInterval = setInterval(function() {
io.emit('sensorReading', {
temperature: sensor.getTemp() + 'C',
humidity: sensor.getHumidity() + '%'
});
}, interval);
console.log('Climate control started!');
}
function stopClimateMonitoring() {
clearInterval(sensorReadingInterval);
console.log('Climate control stopped!');
}
return {
start: startClimateMonitoring,
stop: stopClimateMonitoring
};
}
I am watching a button for changes of state like this:
button.watch(function(err, value) {
led.writeSync(value);
if (value == 1) {
monitorClimate().start(1000);
} else {
monitorClimate().stop();
}
});
The problem is that even after the monitorClimate().stop() call, setInterval keeps getting triggered, thus SocketIO keeps on emitting the sensorReading event.
What am I doing wrong here?
Every time you call monitorClimate() you are creating a new set of functions so monitorClimate().start() and monitorClimate().stop() are not working on the same interval. Try something like:
var monitor = monitorClimate();
button.watch(function(err, value) {
led.writeSync(value);
if (value == 1) {
monitor.start(1000);
} else {
monitor.stop();
}
});

Categories

Resources