I've been trying to create an input in an AngularJS template and that will only accept whole numbers as input. That is, I don't want it to allow any keys other than 0-9, specifically, I can't stop . from being allowed in the input.
Alexander Puchkov created a directive that achieves this on inputs with type="text" however I want to be able to use type="number" so I can maintain all my other attributes on the field for validation such as min, max, step, etc.
I have an example of this directive not working on a number input here. For example, when type="text" an input of 123. yields a rendering of 123 however when type="number" an input of 123. yeilds 123. since the previous value of 123 is equal when compared numerically.
I'm afraid this simply isn't possible as the following condition is true:
0. == 0
If you perform the following:
setTimeout(() => console.log(element[0].value), 100))
It will always log 0. as 0. This is why ngModelCtrl isnt triggering the parser as no changes are detected.
I would suggest not directly modifying the value of ngModel (as this can also end up leading to users putting in invalid data. ie. pasting 12.00 will resolve to 1200 with your example)
I would add a directive that applies validity depending on if a decimal point is used (technically speaking, typing 0. isn't actually using it. 0.01 is). Set the validity to false if there is a decimal point in the number and display an error message accordingly (via ngMessages). This way the user can correct their own error and can learn from the mistake.
Related
In Chrome (or other Chromium browser) when I try to get a value of a number input field the value is empty when the last char is a dot (.).
Here is the simplest example I could think of:
<input type="number" oninput="console.log(event.target.value)">
In Chrome when typing for example "123.45" will result in this output:
1
12
123
123.4
123.45
In Firefox I get something more like I would expect:
1
12
123
123
123.4
123.45
Getting valueAsNumber instead of value will result in a NaN if the last char is dot, so no success there either.
Is there a way to get what is is the actual value of the field (or at least the value without the dot) and not something that is already parsed somehow?
UPDATE:
Thanks to #Kaiido I'm a little closer as to why this happens
In my Chrome browser navigator.languages is set to ['en-US', 'en', 'nl'] in Firefox it's set to just ['en-US', 'en']
This explains the difference between the 2 browsers (in my case) and why in Chrome I can use , as well as ..
But I still need a solution for the problem.
The most important thing in my case is that I need a distinction between the field being empty or some other value that somehow can't de parsed to a number. So now the question is more is there a way to get the value of what's actually being types in the field.
Use the 'step' attribute to make it validate and use the locales delimiter.
<input type="number" step="0.01" oninput="console.log(event.target.value)" />
More information:
<input type="number" oninput="console.log('VAL: %s|%s|%s', this.value, this.checkValidity(), this.validationMessage)" step="0.1">
Entering a delimiter not used in the current locale the validity and corresponding message will indicate 'this is not a number', once more digits are entered - up to the limit of precision the step attribute allows - it will parse to valid values again. Your GUI should correspondingly indicate the current validity - if your code requires it to always be usable as a valid number you could save the last value that passed validation, depending on your use case.
Also consider the use of the character "e" which may be cause for a temporary invalid value!
There are lots of questions like this on StackOverflow, but none of them captures all of my requirements in the same solution. Any help appreciated.
The problem
In my React app, I need a text box with the following characteristics:
It only allows digits to be entered - no minus signs, decimal places, letters, or anything besides just the digits 0-9.
It automatically brings up the number keypad on iOS and Android
I can further restrict the numbers that should be entered, e.g. only allow 4 digits
Leading zeroes are automatically trimmed, e.g. if a user types 02 it should correct to just 2
It allows an empty textbox, and can differentiate between empty and a value of 0
Current code
https://codepen.io/micahrl/pen/RwGeLmo
This code allows typing non-digits, and will just interpret the value as NaN. For instance, the user can type 2f or asdf and the page will say You typed: NaN.
Additionally, while the page loads initially with an empty text box, the user cannot type something and then delete it back to empty. Attempting to delete all text in the input box places a 0 in the box.
Finally, this code doesn't reliably trim leading zeroes, which causes me particular problems because I want to restrict the number to four digits. Typing 01 will not truncate the leading zero; on some browsers, typing 01111 will result in 1111, which is good enough, while on others, typing 01111 will result in 0111, which is a problem.
What I've tried
Because I have set type="number" on the input element, if there is ever a non-number added to the text box, event.target.value in setNumWrapper will be an empty string. This means I can't differentiate between a true empty string (where the user has deleted all text) and invalid input (where the user has typed a non-number, like asdf).
I could set type="text" on the input element, except that I think I need to set it to number to get the number keypad on mobile OSes like iOS and Android.
With further experimentation, and some help from #Keith in a comment, I've solved almost all my problems.
I've forked the codepen in my question and made these changes in the fork: https://codepen.io/micahrl/pen/GRjwqdO.
Checking input validity
#Keith pointed me to validity.badInput (https://developer.mozilla.org/en-US/docs/Web/API/ValidityState/badInput). With this, I can differentiate between empty input, where a user types something then deletes it, and bad input, where the user attempts to add a non-numeric character.
That means I add this to the beginning of setNumWrapper():
if (event.target.value === "") {
if (event.target.validity.badInput) {
// Set the text box and number to the old value - ignore the bad input
inputRef.current.value = String(num);
setNum(num);
} else {
// The data in the text box was deleted - set everything to empty
inputRef.current.value = "";
setNum(NaN);
}
return;
}
I also have to make an inputRef with useRef(), and set it on the <input> element.
This solves #5 most of #1 (but see below for one remaining minor problem).
Trimming leading zeroes
All I had to do for this was use that inputRef to set the value in the <input> element at the end of setNumWrapper():
inputRef.current.value = String(newNum);
The <input> element's value is always a string anyway, and casting the number to a string removed leading zeroes, if there were any.
Remaining problem: invalid input is allowed if the text box is empty
When the text box is empty, the user can type non-numeric characters into it, and setNumWrapper() doesn't even fire. If you put a console.log() at the top of setNumWrapper(), it won't print anything to the log if the user types a letter, but it will print to the log if the user types a number.
This means I cannot use setNumWrapper() to solve this problem.
However, it's also relatively minor. On mobile, the number keypad comes up, preventing non-numeric input. On the desktop nothing stops the user from typing letters with their keyboard, but for my app, it's clear that only numbers are allowed, so this is good enough for now.
Still, if there's a way to fix this, I'd be curious to hear about it.
I'm trying to pull the number that a user types into a text field and multiply that by a number that is already established. (There are other buttons that add +1 or subtract -1 from the total that work just fine. The only problem I'm having is this right here, getting a user's input by them typing in a value to a field and pulling it)
Here's my code:
<!-- HTML Field that I am trying to pull a number out of -->
<input type="text" id="multNumInput">
--
// Creative my variables
var number = 0;
// Creative a variable that is equal to whatever is inputted into the text box
var multNum = $("#multNumInput").val();
// On Button Click, take the number variable and multiply it times whatever the value was inputted in the html
$('#multiply').click(function(){
number = number * multNum;
$('result1').text(number);
console.log(number);
})
Hopefully this is clear enough to understand. As of right now, whenever I click the button, it always changes the number back to 0. That's it. Just 0. No matter what I set the num var to, when clicking the mult button, it always reverts to 0.
You have to convert to number first.
multNum = parseInt(multNum);
number = number * multNum;
//...
First of all, the obvious: Multiplying any number with your number variable which has 0 value will lead to 0 at all times - I suppose you know this but was confused or missed that part, probably by confusing it to initialization of 0 before an addition process. Set it to 1 or another non-negative number and you will probably get better results on the way. :)
In addition, to make it multiply correctly in JS, you have to multiply between two numbers.
Your value is of type String as inserted in your input field.
As already suggested by #Si8, you need to parse it to Number by doing:
multNum = parseInt(multNum);
Also, you seem to be using a text type for your input.
I suggest you set it to a number type, so that you restrict input values:
<input type="number" id="multNumInput">
Check out the Mozilla MDN web docs for more on this.
The answer, as provided by #Robin Zigmond in a comment is this:
"You're failing to convert multNum from a string to a number."
Our stack is currently: React SPA/Apollo/GraphQL/Rails/PostgreSQL.
We have a web form with a number input that maybe (or not) be used to entry decimal values (dynamic/user-created form).
We want the form to be lenient (it has an autosave feature). If user types ,3 or 17,, that is considered a valid number value. But not too lenient, the number should by parseable as a float and be able to display in graphs...
Also, when the user visit the form again, we want to make sure to init the number input with the exact same value he typed initially (I mean, ,3 does not become magically 0,3)
I'd like to know, for our stack, how to serialize (sending as GraphQL payload) and store (in PostgreSQL) and manipulate (in JS/Ruby) such number without loosing the initial user input value.
I'm thinking it would be simpler to send/store a string, as long as it's validated that it can be parsed.
Just wondering if there is a "proper" solution, for example using number libs, custom GraphQL scalar type, decimal SQL type... that seems complicated to me, anyone did something similar before?
Create two fields. One for user input and other with parsed value. I do not think this will be different for any stack.
The challenge is how to show to a user how you understood entered value. This could be solved with a hint with a parsed value under the text field.
you should choose type of the input as "text" instead of number, so you can create a specific format for your input. first, add a property to your state
state={amount:""}
input section for amount in your form should be like this:
<input
type="text"
placeholder="Amount"
value={this.state.amount}
onChange={this.onAmountChange}
/>
onAmountChange = e => {
const amount = e.target.value;
if (!amount || amount.match(/^-?[0-9]\d*(\.\d{0,2})?$/)) {
this.setState(() => ({ amount }));
}
};
regexp in above function means start with any number with optional -, and optionally u can end with 2 decimal number. you can change it for your form.
if you want to add only positive numbers if your form is for price you can use this regexp:
amount.match(/^\d{1,}(\.\d{0,2})?$/)
I am working on textfields and restricting the max length using enforceMaxLength configuration.
So, as a usecase - I am restricting the user to enter only 14 characters. But my textfields appends .00 and a dollar sign once the value is set using this.setRawValue(Ext.util.Format.currency(this.getValue()));
So, after the value is set, the total length of the textfield becomes 18 and it always gives max length violation error message.
Is there any way to avoid the error message but still restrict the user to input only 14 chars? I am trying to clear the maxLength text but I am not able to find a method or a work around to do so.
Any pointers would be helpful.
You can override the validator function of the textfield and call textfield.validate() when you apply the format.
http://jsfiddle.net/FcXrL/1/
That is a proof of concept, you will want to update the regex.
Now, if you can cook up a good regex and get it to validate in one line you can always use a VType. Just make sure you call textfield.validate() when you set the currency, as changing the text with the format util does not cause the validate function to run.
UPDATE
So I searched high and low for a built-in way of doing this, but this was as good as I can get it.
What we are going to do is catch the keypress event on the textfield, and test how many chars we have. If we go over the threshold (14 in this case) we just .setValue() with a substring of the current input.
http://jsfiddle.net/vYu4G/5/
In this fiddle, I broke the textfield out into its own variable so I could easily do the .on(). If you are using MVC you can just listen for the keypress event in your controller.
Additionally note that there is no enforceMaxLength or maxLength config, and I added the enableKeyEvents config.