I would like to set a timer for the action function. The rule is: when the selected text does not change for 3 seconds, we run the function action with the selected text.
I tried the following code in the playground https://microsoft.github.io/monaco-editor/playground.html, it did not work. When I changed the selected text very quickly, their actions were not cancelled.
Could anyone help?
const editor = monaco.editor.create(document.getElementById('container'), {
value: '1+2+3+4+5+6+7',
language: 'javascript'
});
function action(x) {
console.log("action", x)
}
let myTimeout
editor.onDidChangeCursorSelection((e) => {
clearTimeout(myTimeout)
console.log("cleared", myTimeout)
let selectedText = editor.getModel().getValueInRange(editor.getSelection())
if (selectedText != "") { // if we are already in this event, the selected text must change
myTimeout = setTimeout(action(selectedText), 3000);
console.log("set", myTimeout, selectedText)
}
})
Edit 1: My component is a class component, so I cannot use hook calls.
You can use lodash's debounce to achieve the effect you want. And use useCallback to make sure you get the same instance of function. I think an implementation like this might work:
import _ from 'lodash';
...
const debouncedAction = _.debounce(action, 3000).bind(this);
editor.onDidChangeCursorSelection((e) => {
let selectedText = editor.getModel().getValueInRange(editor.getSelection())
if (selectedText != "") { // if we are already in this event, the selected text must change
debouncedAction(selectedText);
}
})
I used this as a reference.
setTimeout takes function as input, while you have called it. I think the following code works well.
const editor = monaco.editor.create(document.getElementById('container'), {
value: '1+2+3+4+5+6+7',
language: 'javascript'
});
function action(x) {
console.log("action", x)
}
let myTimeout
editor.onDidChangeCursorSelection((e) => {
clearTimeout(myTimeout)
let selectedText = editor.getModel().getValueInRange(editor.getSelection())
if (selectedText != "") {
// "action" is called inside a function
myTimeout = setTimeout(() => action(selectedText), 3000);
}
})
Related
I am using a debounce function in my react native project. It gets triggered after user enters 3 characters but it is triggering after 2 sec instead of the delay i assigned to it.
constructor() {
this.init()
this.hitSearchApi = debounce(() => {
log('inside if is called debounce')
this.getStudentTableData(() => this.updateFetchingStatus(true))
this.updateSearchLoaderStatus(false)
this.updateFetchingStatus(false)
}, 100)
}
I had created my debounce function inside the store constructor like this.
#action
onChangeSearchText = (searchText) => {
if (get(searchText, 'length', 0) > 2) {
log('inside search text if condition')
this.updateSearchLoaderStatus(true)
this.hitSearchApi.cancel()
// this.updateSearchLoaderStatus(true)
log('before hit search api')
this.hitSearchApi()
// fetch student list on search basis
} else if (get(searchText, 'length') === 0) {
this.setStudentListData({})
this.updateFetchingStatus(true)
this.resetSearchData()
}
this.searchText = searchText
}
This is my onChange text triggered, which is getting called on textinput onChange event.
Please help me out in this. What am I doing wrong here?
I've tried to decode the code I've been studying for a while. But, probably, because of my beginner mindset, so I still cannot understand how it works.
Here's my question. The word 'text' inside function addTodo('text').
Where does it come from? or it's declared as its own entity for itself. And what is it for?
Thank you in advance
function addTodo(text) { // The 1st 'text'
const todo = {
text, // the 2nd 'text'
checked: false,
id: Date.now(),
};
todoItems.push(todo);
renderTodo(todo);
}
const form = document.querySelector('.js-form');
form.addEventListener('submit', event => {
event.preventDefault();
const input = document.querySelector('.js-todo-input');
const text = input.value.trim(); // the 3rd text
if (text !== '') {
addTodo(text);
input.value = '';
input.focus();
}
});
function addTodo(text) is the signature for the 'addTodo' function. It takes one argument, which is called text.
This argument is then used within the function. It's provided as the value of a property to an object called todo (this is a bit of a short-hand in JavaScript)
The 'third' use of text, is created as result of applying trim to the value property of the input. This text could have been called something else.
This use of text is then passed to the function we previously talked about, assuming it's not empty.
You could equally write the code as follows - it would work just the same
function addTodo(textForTheTodo) { // The 1st 'text'
const todo = {
text: textForTheTodo, // the 2nd 'text'
checked: false,
id: Date.now(),
};
todoItems.push(todo);
renderTodo(todo);
}
const form = document.querySelector('.js-form');
form.addEventListener('submit', event => {
event.preventDefault();
const input = document.querySelector('.js-todo-input');
const textFromTheInput = input.value.trim(); // the 3rd text
if (textFromTheInput !== '') {
addTodo(textFromTheInput);
input.value = '';
input.focus();
}
});
How can I spy on the focus() and select() functions within Jasmine (Unit testing)?
My function:
static nextFieldOnAlt(e) {
if (e.keyCode === 18) {
const focusableFields = Array.prototype.slice.call(document.querySelectorAll('input, textarea'));
const indexFocus = (focusableFields.indexOf(document.activeElement) + 1) % focusableFields.length;
const input = focusableFields[indexFocus];
input.focus();
input.select();
}
}
The unit test:
describe('nextFieldOnAlt function', function() {
it('check', function() {
const event = {
type: 'keypress',
keyCode: 18
};
const focusableFields = angular.element(['<input type="number">22</input>', '<textarea>Test</textarea>']);
spyOn(document, 'querySelectorAll').and.returnValue(focusableFields);
Utilities.nextFieldOnAlt(event);
expect(event.keyCode).toEqual(13);
});
});
I get the error
focus is not a function
Tried to add spy like the querySelectorAll, but that didn't work.
Any ideas, suggestions?
I'm guessing a bit, but this looks wrong to me:
const focusableFields = angular.element(['<input type="number">22</input>', '<textarea>Test</textarea>']);
I think you want an array of Angular elements, so I suspect if you change it to this:
const focusableFields = [angular.element('<input type="number">22</input>'), angular.element('<textarea>Test</textarea>')]);
Then, I suspect this code will return a proper array of Angular elements, and this code in your component will return valid objects with a focus field:
const focusableFields = Array.prototype.slice.call(document.querySelectorAll('input, textarea'));
I would strongly recommend stepping through code with a debugger to help address what is occuring.
I'm building an autocomplete feature. Everything works as expected except when I reach the end of the input. When I change the value of the input, programatically, the input doesn't scroll/focus on the cursor until I type something.
I tried doing something like this on componentDidUpdate and it worked, but besides being a bit "dirty" I don't want to use the onBlur because I'm closing the autocomplete popup on this event.
setTimeout(() => {
this.refInput.input.selectionStart = this.refInput.input.selectionEnd = this.state.value.length;
this.refInput.input.blur();
this.refInput.input.focus();
}, 1);
How can I achieve this, specially without a setTimeout? If I do it without the setTimeout it doesn't work.
I'd do it with a useEffect Hook
import React, { useEffect } from 'react'
//
//
useEffect(()=>{
this.refInput.input.selectionStart = this.refInput.input.selectionEnd = this.state.value.length;
this.refInput.input.blur();
this.refInput.input.focus();
},[this.state.value, this.refInput.input.selectionStart, this.refInput.input.selectionEnd])
I made it worked. Unfortunately it is not very pretty. I still need to use the blur and setTimeout events.
onBlur = () => {
setTimeout(() => {
if (this.refInput.input !== document.activeElement) {
console.log(this.refInput.input.selectionEnd);
this.setState({ helperVisible: false });
this.props.clearAutoComplete();
}
}, 1);
};
(...)
componentDidUpdate(prevProps, prevState, snapshot) {
const { value } = this.state;
if (prevState.value !== value) {
setTimeout(() => {
this.refInput.input.selectionStart = this.refInput.input.selectionEnd = this.state.value.length;
this.refInput.input.blur();
this.refInput.input.focus();
}, 0);
}
If anyone has any ideia how to improve this it would be great. Otherwise I'm marking this as the correct answer.
The below should grab the element. focus on it. then move the selection range to the end of the current value. This abides by the desire to avoid blur().
CAVEAT: sometimes it doesn't seem to properly fire in this snippet and in codepen..
(function () {
const el = document.getElementById('dataList'),
val = 'this is my value',
len = val.length;
el.focus();
el.value = val;
setTimeout(function () { el.setSelectionRange(len, len); }, 100);
})();
<input id="dataList">
I don't have a provision to not invoke the JS that tries to set a value of a field using:
getElementById(field_name).value = 'someValue'
So, as an alternative, I'm trying to see if there's a way to intercept/override the JS that sets the value of the field in the html and try to restrict it if certain conditions are met.
Please let me know if it's possible?
It may seem that this question is a duplicate of this and this but it's not. Those question's solution wants a change in the code, which is not an option for me.
I'm afraid you can't, so if you really cannot change the code setting it, you're stuck with the really ugly option of polling it and watching for changes. E.g., something vaguely like this:
function polledInterceptor(id, callback) {
var element = document.getElementById(id);
var value = element.value;
var timer = setInterval(function() {
if (value !== element.value) {
value = callback(element, value);
if (value !== null) {
element.value = value;
}
}
}, 30); // 30 = 30ms
return {
stop: function() {
clearTimeout(timer);
}
};
}
...and then use it like this:
var poller = polledInterceptor("field_name", function(element, oldValue) {
var newValue = element.value;
// ...decide what to do...
return "someValue"; // or return null to leave the value unchanged
});
when ready to stop:
poller.stop();