This question has a JSFiddle: jsfiddle.net/redmeat/L6p85fjc.
I'm attempting to make a server call when CodeMirror loses focus, but I first want to check if the value has changed.
CodeMirror provides an API for testing whether a value has changed, including whether the value was undone:
doc.changeGeneration(?closeEvent: boolean) → integer
doc.isClean(?generation: integer) → boolean
From the documentation: changeGeneration returns a number that can later be passed to isClean to test whether any edits were made (and not undone) in the meantime.
However if changes are reverted using the backspace or delete keys rather than undo and redo shortcuts, the form is considered not clean even if a change generation number is provided to isClean and the value is back where it started.
This JSFidlde shows that the values can match, but unless undo or redo shortcuts (ctrl+z and ctrl+y) are used, the form is considered dirty.
Open the JSFiddle.
Add a character to the end of foo.
Next, either:
Use the backspace key to remove the character.
OR
Use the Ctrl+z shortcut to remove the character.
In both scenarios the final value is the same as the original value. But it is only in the second scenario where isClean is true.
Question: Is there any way to check if the value is different? I naively expected isClean + changeGeneration to help here :/
Related
As far as I can tell there isn't, but I figured I'd ask.
I have a text input. Autocomplete suggestions are fetched dynamically as you type and fill a datalist attached to the input. Normally, typing something and pressing the "search" button brings up a table of search results to select from.
Since the datalist is basically the exact same thing, but simplified, and selecting an option from it is unambiguous, I'd like it to just carry on with my selection handlers without having to bring up the list for selection a second time. When the person manually types something though, I still want them to explicitly pick from the list, especially since some options may be substrings of the others, so I don't want it to auto-select a result for you if it matches halfway through.
I ended up not reimplementing it like ControlAltDel suggested in his comment and instead went with the following slightly hacky but functional solution:
Since I am refetching the search results as you type, if only 1 search result is returned (ie. it's unambiguous) and the current string is a case-insensitive exact match to that result, then select it. It works well for what I need it for, but I could imagine this not working for everyone.
The JS is roughly as follows:
if (searchResults.length === 1
&& searchString.toLowerCase() === searchResults[0].toLowerCase()
) {
selectResult(searchResults[0]);
}
I'm calling this in my handler for when the search results list changes, not the input's handler, since the results are only re-fetched after the input has already been changed.
Background: I have stored a key modifier and a keycode value, like so ["shiftKey", 32] (=> shift+space), or [undefined, 32] (=> space) on the user's machine. This array that I have stored acts as a "hotkey", i.e., I'm supposed to detect when user presses any key that matches with the hotkey. So, I'm supposed to do something when user presses Shift+Space, for example. Functionality wise it is working fine so far, as the keyCode's still match. The only problem is that...
Problem: I cannot properly display the keyCode in text form. ("?" key has keyCode=191 on my system, but it does not display correctly on doing String.fromCharCode, obviously)
My proposed solution: convert the [key modifier, keyCode] array to [key modifier, key, code] (since key and code properties can easily be displayed as text). The conversion code that I will write will run on the user's machine (it's a Chrome extension), so there is no worry of not having the locale information of the user. The problem is: how exactly to perform this conversion?
My attempt: dispatch programmatic keyboard events to a fake textbox based on the keyCode, and using the key and code values generated from there as the conversion result. However, the only properly working fiddle I could find required me to actually supply the key and code values, otherwise they would be undefined.
Final question: how else to perform this conversion? Or is there any other workaround to achieve what I want to?
I've recently upgraded the ngx-bootstrap from 1.8.1 to 3.0.1 . After the upgrade type ahead doesn't seem to work as expected. I'm using this example :
https://valor-software.com/ngx-bootstrap/#/typeahead#async-data
with [typeaheadMinLength]="3"
Now , if I search, lets say "abcdef" then it starts searching after 3 characters have been typed that is abc and then abcd, abcde, abcdef and so on which is fine.
But now if I delete everything in input textbox using backspace in one go, that is if I make abcdef to empty by pressing backspace in one go, then once input is empty, it shows drop down values again which correspond to min length which is abc.
Ideally it should clear drop down values but looks like when you delete it very fast using backspace, it retains the values corresponding to min length token string.
It is more visible when data is fetched from a service and the data is huge, so it takes some time to load and clear.
Delay in service response can be emulated using typeaheadWaitMs and this issue can be replicated using this example : https://valor-software.com/ngx-bootstrap/#/typeahead#delay
https://github.com/valor-software/ngx-bootstrap/issues/4412
Could someone please help on this?
You have to put a check if search field is empty then clear the list holding values. When pressing backspace what happens is that when search length reaches threshold value i.e abc it fetches the result and stores it after that no operation is performed hence the search results for abc are persisted. Add (keyup)="onKey($event.target.value)" if value is empty clear the list holding your dropdown data.
As a workaround, I removed [typeaheadMinLength]="3" and instead checked the length on server. If length of prefix token is less than 3 , server doesn't do anything and instead returns empty array. This isn't the optimal solution ofcourse because even for length less than 3, requests will go to the server.
Although, I didn't feel any visible performance impact but still it could be better if done on UI rather than server.
I've been dissatisfied with the way Dijit's FilteringSelect widget works for some time and have been tinkering using the Dojo 1.10 trying to improve it for my use case. Unfortunately it seems no combination of settings is quite right, largely because they don't work together.
Setting queryExpr: '*${0}*' is nice, but it make auto-complete go loony.
Setting autoComplete: true is nice as long as you want to type the whole text starting from the beginning until you find your match. Unfortunately if you want to start in the middle somewhere it becomes a pain in the butt. Sure you can set searchDelay: N to to something large enough to catch all your typing, but as soon as you let it return incremental results in the menu, BAM your ability to keep typing and maybe end up with a match somewhere else in the word goes out the window.
What I really want is something that completes much like a fuzzy-finder does in a shell or decent text editor (e.g. fzf). Such finders skip over intermediate characters, basically splitting your input by character and adding implicit wild-cards between them. You keep typing until the first match is the one you want, then end the finder and let it replace the value.
I started messing with a way to implement this, but didn't get very far. I thought about hijacking _patternToRegExp(), but quickly discovered that my store (an instance of dojo/data/ItemFileReadStore with some JSON data) sets the _oldAPI flag and that never gets executed. I'm happy to update stores, but it isn't readily apparent to me that will make this easier. Hacking on my store, things spiraled out of control and I decided to take a less involved but more hackish approach.
If you turn off auto complete and set the options to do matching in the middle of words, you get a results list pretty close to what is needed. All that remains for the user to do is hit Down once after they type in enough input to get a match and before they Tab away. The question then becomes how to avoid requiring this manual intervention and become more forgiving.
define(["dijit/form/FilteringSelect"], function(FilteringSelect){
return declare("alerque.FuzzyFilter", [FilteringSelect], {
autoComplete: false,
highlightMatch: 'all',
ignoreCase: true,
queryExpr: '*${0}*',
searchDelay: 0,
_patternToRegExp: function(qs) {
// If this ever actually got called, maybe we could
// return qs split with wild cards between all characters
return this.inherited(arguments);
},
onblur: function() {
this._autoCompleteText(this.get('displayedValue'));
// Pick first match from menu
return this.inherited(arguments);
}
})});
Hijacking the onblur() function seems to be the right place to make a widget that defaults to the first match if you tab or click away, but I can't figure out how to actually use the first match from the menu.
How should I proceed to get more robust fuzzy searching with auto-completion of the best match? I don't want a ComboBox, the value has to end up being one of the values in my JSON data set. At the same time I want input options to be much sloppier than typing the value from the beginning or having to manually select a match.
maybe a workaround/solution for your onBlur :
set the queryExpr="\${0}"
no delay and autocomplete off
On onkeyup of the filteringSelect you store the first value from the popup matches somewhere, then after onblur change the displayedValue/value of your filteringselect to the value from the first popup (if it was found & matches...)
To get the first value from the popup:
The first shown value can be found in a element with id = YOUR_FILTERING_SELECT_ID + "_popup0"
so if your id = "mySearchData" then look for id "mySearchData_popup0"
if that element exists then store the innerHTML somewhere (hidden element or var ...)
Adjust value from innerHTML to match value from store:
From the value you get from the innerHTML, remove the span elements from it so it matches one of the values of your datastore
if your id from your filteringselect = "mySearchField" and if you are searching "123" and the first match in popup shows "test 123 number"
then the innerHTML value from the first popup will look like this
<li id="mySearchField_popup0" class="dijitReset dijitMenuItem" role="option">
test
<span class="dijitComboBoxHighlightMatch">123</span>
number
</li>
So, little bit of String doodling (just remove the span tags out of the innerHTML value) and you will have a value that matches your first result after onblur.
Is there a way to apply a mask in a input using yui3 ?
Is it possible to have a field where the user can only enter a phone number?
And if so, how?
Thx a lot
I would say that your best bet is to have an onChange or onKeyup (or even onValuechange - a YUI construct) handler listening on that input. Whenever it detected a change, you would run a formatting function on the current value of the input, which formatted it in the way you wanted.
if you want to be light-handed about it, just put the dashes in where they go, for example :
"1105551212" --> "110-555-1212"
if you want to be heavy-handed about it, the event handler could literally strip out any non-numeric, or non-dash characters, which effectively prevents the user from entering bad input, though they could of course put in a non-existent phone number.
one step further: do both. strip out invalid characters, and do auto-formatting.