I am trying to reproduce in my app a UX similar to Microsoft Excel / Google Sheets when you type of formula and you have an autocomplete dropdown for the different formulas and variables you have at your disposal.
For that purpose, after having validated the autocomplete, I want to be able to control the position of the cursor.
For exemple, if I type =sum(variable1, variable2), on autocomplete of variable2, cursor should be before the last parenthesis and not at the very end.
I understand how to set the position of the cursor with javascript, the problem is since at the same time I modify the value of the input and set the cursor position, the latter doesn't work.
I reproduced on fiddle the problem with a simpler context: https://jsfiddle.net/joparisot/j8ourfa1/31/
My html:
<div id="app">
<autocomplete v-model="selection"></autocomplete>
</div>
<template id="autocomplete">
<div>
<h2>gernerogrnio</h2>
<input id="my-input"
class="form-control"
type="text"
:value="value"
@keydown.enter="updateValue($event.target.value)">
<p>{{ value }}</p>
</div>
</template>
My script:
Vue.component('autocomplete', {
template: '#autocomplete',
props: {
value: {
type: String,
required: true
}
},
methods: {
updateValue (value) {
var new_value = ''
if (value.length < 4) {
new_value = 'Inferior'
} else {
new_value = 'Superior'
}
this.$emit('input', new_value)
var myInput = document.getElementById('my-input');
this.setCaretPosition(myInput, 5)
},
setCaretPosition(ctrl, pos) {
ctrl.focus();
ctrl.setSelectionRange(pos, pos);
}
}
});
new Vue({
el: '#app',
data: {
selection: 'test'
}
});
I don't bother there with the autocomplete, but depending on what you type, when you press enter, the input will be filled with a new value. You can see that if you comment lines 11 to 16 and just set new_value to value, then setting the cursor position will work.
I can't seem to be able to do both things at the same time. Any thoughts?
Thanks to Roy J's comment, I was able to find the solution.
Add the following in the updateValue function:
I learned about
setSelectionRange
from this question, and I used it to handle credit card number input:template:
instance methods:
The goal with the above code is to add spaces into a credit card input, so
1234123412341234
is automatically reformatted to1234 1234 1234 1234
. A person venturing into this territory will notice that problems arise when editing the input value.You can see there are three conditions in my sample above. The last one is the default which simply reformats the current value with a 2-step combo: remove all spaces then adds a space every 4th character.
If you comment out the two
if
blocks, you can watch the problems emerge.The first
if
block handles the backspace event. As you can see, every time the input changes, the value is captured asthis.lastValue
. When you press backspace, the goal of the first condition is to NOT run the regex. In my opinion, this is better UX. If you comment out that condition, you can see.The second
if
block handles the editing events. A good way to test it, is to enter a valid CC but omit the 3rd character, so that everything is off by one. Then add the character in. Everything should be good. Likewise if you backspace multiple characters out. The goal of the second condition is to properly manage the cursor position (or caret position if you prefer that nomenclature).You can safely delete the first condition and all references to
lastValue
and the code will still work. This is arguably simpler but worse UX.