I'm using one of the new HTML5 input types, number:
<input type="number" />
In Opera, which is the only desktop browser I know of which currently recognises it, it renders it as such:
The problem I'm facing is one of validation. If a user types something invalid into the field, eg: "abc", the value returned by myInput.value is an empty string. This makes it impossible to tell whether the user left the field blank, or if they entered some incorrect data. Is there any way to get the real value of the field?
The HTML5 draft defines:
The value sanitization algorithm is as follows: If the value of the element is not a valid floating point number, then set it to the empty string instead.
Reference1
I suppose you'd have use a default value of "0" to make sure the field was left untouched or if something invalid was entered, since there seems to be no obvious way to differentiate the two.
After reading up validation specs and some testing (in Opera 10.54) I concluded that:
<input id="email" type="email" value="blah">
document.getElementById("email").validity.typeMismatch // True
Doesn't work on <input type="number">. Not sure if it's supposed to, or if it's a work in progress. The property does however exist, though it always returns False.
Read more2
You can also set a custom validation method Reference3
Related
I am developing a touch keyboard for a project and running into this issue which I have not been able to resolve in an acceptable way.
The main issue is when I type a decimal on the touch keyboard on a number field, the input clears because when I set the text through JavaScript: htmlinputelement.value = "161."
I get the the following error: The specified value "161." cannot be parsed, or is out of range.
I've seen a few similar questions (The specified value cannot be parsed, or is out of range when using number pipe , The specified value "." cannot be parsed, or is out of range, and a few others)
The problem is resolved if I changed the type of the input from 'number' to 'text'. But doing this then opens up other issues. Some of the input fields use the step property and we have other input validation which can't be used if the type is not 'number'.
So my questions are
How can I type (using a usb keyboard) a decimal in an input field of type number without this issue? (How does the HTML input field handle value changes). I ask this since I am able to type 1. without issue but when I set it programmatically, it parses the value and fails.
Is it possible to bypass the HTMLInputElement value parse when setting it?
Edit:
My newest solution which seems to create the least amount of edge cases/other issues is to temporarily change the input type to 'text' to allow the decimal display, then change the type back to number after the next input. Let me know you guys thoughts on this!
You can use parseFloat() (or even parseFloat().toString() if you really want).
It's a bit longer than a +"0", but you don't have to check for the presence of ..
function doTest(event){
document.getElementById("input").value=parseFloat(event.target.innerText);
}
<input type="number" id="input"><br>
<button onclick="doTest(event)">1</button>
<button onclick="doTest(event)">2.</button>
<button onclick="doTest(event)">.3</button>
<button onclick="doTest(event)">4.5</button>
I want to set an invalid value to input element with type=number.
I'm writing unit tests for my component which uses input with type=number and it behavior depends on whether the input is empty or not not. Note that I'm treating invalid values like '1.2.3' as not empty, even though it's value property is ''.
While it's possible for user in browser to enter invalid values like '1.2.3' I cannot simulate it for my tests.
// let input be HTMLInputElement
input.value = '1.2.3';
console.log(input.value); // ""
console.log(input.validity.valid); // true
I would like to somehow set invalid value for my input type=number, so I could test if my component's logic is valid.
You can use
input.setCustomValidity("dummy value");
to make input.validity.valid return false.
For an idea of why this works, see this CodePen. What it does is it sets an error message that indicates the input is invalid. To make it appear valid once again, use
input.setCustomValidity("");
I have a simple controlled input of type number like below.
<input type="number" value={+value} step={1} onChange={this.updateMyChange} />
My value often returns a decimal number like 123.123 . My problem is, when I try edit the value. The cursor loses focus and shifts to the beginning ignoring the whole numbers as soon as the decimal places are cleared. Like below:
How do I address this? Immediately after the decimal places are cleared, the cursor jumps to the beginning thereby making it impossible to edit the whole numbers. Any help would be appreciated.
Update
Below is the remaining code as requested by the user below.
render() {
const {value} = this.state;
return (
<input type="number" value={+value} step={1} onChange={this.updateMyChange} />
)
}
And my updateMyChange method is simply
updateMyChange(e) {
this.setState({ value: e.target.value });
}
It does nothing much simply sets the new value. The cursor position jumps to the end as soon as decimal places are cleared. It does not set cursor for whole numbers.
This is how React updates an input field's value:
node.setAttribute(attributeName, '' + value);
When you set value attribute using that method, the caret goes to the beginning of the field, regardless of using React. You can see what I am saying in this fiddle - https://jsfiddle.net/5v896g3q/
(Just try and position the cursor in the field, between changes).
According to MDN, setAttribute is unstable when dealing with value. The recommended way of changing value is by accessing the value property of the element, like element.value = newValue. If you use that approach, all seems to go as expected.
This is all I can tell for sure. Now let's speculate a little. When you type anything in that field, you are:
updating the value of the input
reading that value, and sending to React as state
React updates the value of the input, with the new state
When you are typing on the field, step 3 is likely to have no impact, because when the value comes back, the input already got it right. Except on the case with the float number. When your field reads 1., the actual value React updates the field with is 1. And React uses the evil method (setAttribute).
So, a workaround I found was setting the value of the field, using the proper method, before React touches it, on componentWillUpdate:
componentWillUpdate(nProps, nState){
this.refs.input.value = '0' + nState.value
}
The problem there, is that it is "numerizing" the value on every change, meaning I won't be able to have a point (1.). For that reason, I will only edit the input in case new value is 2 characters shorter than the old one (point + digit after point):
componentWillUpdate(nProps, nState){
if(this.state.value.length - nState.value.length === 2){
this.refs.input.value = '0' + nState.value
}
}
Working example - https://jsfiddle.net/bsoku268/3/
note: the fiddle is for demonstration purposes, and not supposed to be a bulletproof solution, as there are many ways of interacting with an input field, such as copy & paste, drag & drop, autofill, etc
In this tutorial by Jenkov, he asserts that you can give a form a name, and then access it in the controller by
$scope.FORM_NAME.INPUT_NAME
For example like
<form name="myFormNg" ng-submit="myForm.submitTheForm()" >
<input name="firstName" type="text" ng-model="myForm.firstName">
</form>
and then in Angular
$scope.myFormNg.firstName
However, when I try it, instead of getting what I typed into the input box (abc), I get this whole object:
{"$viewValue":"abc","$modelValue":"abc","$$rawModelValue":"abc","$validators":{},"$asyncValidators":{},"$parsers":[],"$formatters":[null],"$viewChangeListeners":[],"$untouched":false,"$touched":true,"$pristine":false,"$dirty":true,"$valid":true,"$invalid":false,"$error":{},"$$success":{"parse":true,"required":true},"$name":"email","$options":null,"$$lastCommittedViewValue":"abc"}
Why? Is there a cleaner way to do it that Jenkov omits?
Actually, Jenkov mentions (and quite clearly) one thing: with $scope.formName.inputName you'll get an instance of ngModelController (and only if this input is already bound - with ng-model or in some other way).
This instance has many uses; you can get back its view value (via $viewValue property; see this demo), check whether or not it passes validation etc. - but it's still an Object, not a string.
OK this is a weird one. I'm sure I'm missing something obvious.
http://jsfiddle.net/wVp8j/
HTML:
<form>
<input type='text' required />
<button>check field validity</button>
</form>
JS:
var field = document.querySelector('input[type=text]');
document.querySelector('button').addEventListener('click', function() {
alert('Validates: '+field.willValidate);
alert('Value missing: '+field.validity.valueMissing);
}, false);
If the form is submitted with the field left blank, submission is suppressed, as you'd expect, but the first alert, checking the field's validity state, gives true. Why?
To contradict this further, the second alert confirms there's a problem with the field and also gives true, as expected.
What am I missing?
[Sidenote: MDN seems to think willValidate is a method, not a property.]
[EDIT]
As the commenter below points out, willValidate says whether the field is a candidate for validation, not, despite its misleading name, whether the field will validate.
Which presumably means the only means of telling whether a field will validate, should the form be submitted, via JS, is to iterate over its validity object and see if any flag is set to true.
[EDIT 2]
Turns out you can just check validity.valid on a field, even though the valid flag doesn't show up if you console.log the entire validity object. This would appear to be the way, therefore, to find out whether a field will hypothetically validate.
willValidate is a property that says whether or not an input can be validated, not if it is valid or not. The only time that willValidate is false is if the input element is disabled or the like.
See http://www.html5rocks.com/en/tutorials/forms/constraintvalidation/#toc-willValidate
Use field.validity.valid instead to check for validity.
http://jsfiddle.net/3xFua/
(function() {
var field = document.querySelector('input[type=text]');
document.querySelector('button').addEventListener('click', function() {
console.log('Validates: ', field.validity.valid);
console.log('Value missing: ', field.validity.valueMissing);
}, false);
})();
The documentation suggests using .checkValidity() which
Method Description
checkValidity() Returns true if the element's value has no validity problems; false otherwise. If the element is invalid, this method also causes an invalid event at the element.
while (as #soktinpk's answer correctly states) .willValidate simply flags it as available for validation, not passing validation.
Thus I recommend using this:
function() {
alert('Validates: '+field.checkValidation());
alert('Value missing: '+field.validity.valueMissing);
}
off-topic, alert is a terrible debugging tool. Consider using console.log or debugger;