Input for currency not treating 0 like other numbers after decimal - html

I'm make a react application that takes an input that should be shown to two decimal places. When the default input has a one number after the decimal, it should add a second number with a 0 after it. It also lets me add infinite amounts of "0"s, but caps any other number at 2 decimal places.
Two different problems pictured:
Case 1.
This is how the input looks when first loaded. The preferred display would be 1.60 (two decimal places)
Case 2.
Current behavior allows the user to add infinite 0s (any other number it caps at 2 decimal places). Expected behavior should cap all numbers including 0s at 2 decimal places.
function ProductMaterial() {
const [name, setName] = useState("");
function handleChange(e) {
let val = e.target.value;
if (val.includes(".") && val.split(".")[1].length > 2) {
}
else {
setName(parseFloat(val));
setMaterialState(parseFloat(val));
}
}
return (
<div className="input-group-sm col-xs-2 material input-group">
<div className="input-group-prepend">
<span className="input-group-text">$</span>
</div>
<input
className="form-control"
type="number"
min="0.00"
onChange={handleChange}
value={product.material}
/>
</div>
);
}
Here is the code for setMaterialState. It changes the product.material value, and then calls setMaterial() in a useEffect() function. The setMaterial() takes the id of the product, and the new material value and saves it to state.
function setMaterialState(newMaterial) {
product.material = newMaterial;
}
useEffect(() => {
setMaterial(product.id, product.material);
}, [product.material]);
I know that the issue here is that on first load nothing is triggering the onChange() function. I've tried setting value={product.material} to value={product.material.toFixed(2)} but it makes it difficult to fix the input. Is there a good way to initialize it before, maybe using a UseEffect() statement? And on top of this I'm not sure why the 0s can be added indefinitely.
Update:
The first problem with the leading 0s was solved by using the onKeyPress= answer suggested by Photonic.
However, I'm still struggling on the forcing it to be two decimal places when there is a 0 at the end problem. I'm realizing that the issue with not starting doing 1.60 is due to it being converted to a float value and not being a string. I tried doing a UseEffect() [] and force it to go to 2 decimal places with product.material.toFixed(); as well as modify the data to start with being 1.60, but it seems like the only way to force the two decimal places is keeping it as a string or converting it to a string by using product.material.toFixed(2). The value in the input value={product.material} need to be a number to correctly execute calculations and collect user input. I'm not sure what the best solution for this is.

I dont know how you are initializing the first load but you can use UseEffect() to fix the decimal place issue like you mentioned.
For the infinite decimal places...
onChange is an event that is already happen, you might want to try changing from onChange to onKeyDown or onKeyPress and in your code you can add e.preventDefault() to stop number from being entered
if (val.includes(".") && val.split(".")[1].length > 2) {
e.preventDefault()
}

If you want to call a function when the 'component did mount' you can use
useEffect(yourFunction(){}, [])

Related

Is there a easy way to handle input type="number" as a default negative value?

I've tried filling the value on init with a "-" sign but only got the Error-Message:
The specified value "-" cannot be parsed, or is out of range.
Another attempt was to just accept every value as negative if it doesn't start with a "+" sign but when reading the value property the sign wasn't included.
Does anyone know an easy way to handle an input type="number" as a negative and only make it positive when explicitly stated?
Preferably in a user-friendly way. I don't want a check-box to handle that.
Specifics:
i have an input Field in html
<input type="number" #testAmount>
I want to handle the value as negative when not explicitly stated otherwise.
so my first attempt was this in TS:
#ViewChild('testAmount') testAmount: ElementRef<HTMLInputElement> | undefined = undefined;
ngOnInit() {
if(!!this.testAmount){
console.log('set amount value');
console.log(this.testAmount.nativeElement.value);
this.testAmount.nativeElement.value = '-'
}
}
that's when i got the error message above:
The specified value "-" cannot be parsed, or is out of range.
Attempt 2:
In my second attempt figured to just accept any value and treat it as negative and only once the user puts a "+" before the value it would be a positive.
but that didn't work out, because when i read the value like this:
console.log(this.testAmount.nativeElement.value)
i was given the value without the + sign, presumably because it was interpreted as a number and thus the + sign was automatically removed.
To Be clear
All i want is that the user doesn't have to add the minus sign every time he adds a value, because negative values will be the norm.
But a positive value shall still be possible it is just rather rare.
Solution?
Best solution i've found so far is to give my input a KeyDown event and handle the very first key-input, it's not perfect but i think it'll get the job done most of the time:
inputHasBeenMade = false
onKeyDown(event: KeyboardEvent) {
if(!this.inputHasBeenMade && !!this.amount){
if(event.key !== "+"){
event.preventDefault()
this.amount.nativeElement.value = '-' + event.key
}
this.inputHasBeenMade = true
}
}
i don't think it's a good solution so i won't write it down as an answer (for now) in the hopes that someone will come up with a better solution.
type number can't have a "-" string.
you can specify -78 or some other number without the ""
Change
let myValue = '-';
this.testAmount.nativeElement.value = myValue
To
let myValue = -5;
this.testAmount.nativeElement.value = myValue;
You are creating a minus symbol when you wrap it with a single ' or a " double quote, and this makes it a string. Remove the quotes and it will be an integer.
You can also look at casting a string to an int if that's required, but not good practice.

How to detect up and down clicks on html input[number]?

I have an html <input type="number"> box that has some custom validation logic. A valid value is any integer x where x < -100 OR x >= 100.
My goal is to implement this behavior:
when the user clicks on the native down arrow or presses the down arrow key and the current value is 100, the value changes to -101.
similarly when the user clicks on the native up arrow or presses the up arrow key and the current value is -101, the value changes to 100.
A few caveats:
Users must still be able to type numbers that fall within the invalid range since they may need to type 10 in order to type 109. And validation logic already occurs for this.
I am using angularjs, but I suspect that the solution is not going to be angular specific.
This is an internal application, meant for Chrome only, so browser specific answers are fine.
I think I have what you need, or at least I'm getting close:
window.onload = function() {
function changeNum(input, typing) {
var lower=-101, upper=100, x=parseInt(input.value), active=(input==document.activeElement);
if ((typing && String(Math.abs(x)).length>2 && lower<x&&x<upper) || (!typing && lower<x&&x<upper)) {
if (Math.abs(x-upper) < Math.abs(x-lower)) {input.value = (!active||typing?upper:lower);}
else {input.value = (!active||typing?lower:upper);}
}
}
document.getElementById("num").addEventListener("keyup",function(){changeNum(this,true);},false);
document.getElementById("num").addEventListener("change",function(){changeNum(this,false);},false);
};
<input type="number" id="num" value="100" />
jsfiddle: https://jsfiddle.net/9zz0ra35/4/
codepen: http://codepen.io/anon/pen/Ndqbog
When the user clicks on the input's up&down-buttons, the value flips over on the lower and upper threshold (-100 -> 100, 99 -> -101).
When the user types a value and then clicks outside the input, invalid values are changed to the closest threshold (-100 -> -101, 99 -> 100).
While typing, invalid values are also changed to the closest threshold, but only if the value.length is more than 2 chars (-100 -> -101).
This last one isn't as clean as the others, because it only works if both the lower and upper threshold have the same length (in this case 3 chars).
But if you need thresholds with different lengths, you can always change the String(Math.abs(x)).length>2 to an extra if-clause and first check whether the value is positive or negative, and then check for separate lengths.
I'm not sure if I'm getting what you want. Is it something like this?
var number = document.getElementById('number-input');
number.onchange = function(event) {
if(number.value > 100) {
number.value = -101;
} else if(number.value < -100) {
number.value = 101;
}
};
<input type="number" id="number-input">

Tally Up Number of Occurrences in Angular

So basically I have this quiz app im working on using angular and I want to tally up the amount of times the right answer is entered. I already made it so the words 'CORRECT' are displayed by the question if they type the right answer in the text box, but I want to see how many times that happens. Here is my code
div ng-repeat="q in questions">
<span>{{ q.question }}</span><br>
<input type="text" ng-model="q.ans" name="email" placeholder="">
<div ng-show="q.ans===q.answer">CORRECT!</div>
<div>
so basically questions is just an array with a question string and answer string. I want to see at the end how many are correct. So I'm thinking, I added in a correct property to the question objects that has a default of 0 which could mean wrong, and change when its right to 1.
Now how would I make it change from the html page here when someone types the right answer? like if correct is shown, if the ng-show is right, then that value would be 1, if not, it'd be 0.
thanks for any assistance. Wondering if I could do this in real time instead of having a 'check' button at the end.
EDIT: okay I looking around the ng-if directive, would it somehow be possible to add like
<div ng-if="q.ans===q.answer">{{ q.correct = 1 }} </div>
or somehow execute that q.correct = 1 (meaning that answer is correct) if the ng-if block is run?
Make a filter for counting the correct answers
// app is your module
app.filter('correctCount', function() {
return function(questions) {
return questions.reduce(function(count, q) {
return count + (q.ans === q.answer ? 1 : 0);
}, 0);
};
})
Then you can display the total in your template
Total: {{questions | correctCount | number}}
Demo ~ http://plnkr.co/edit/br3fxHQ8q04ajZj6Fxch?p=preview
An alternative to reduce that might be easier to understand is...
return questions.filter(function(q) {
return q.ans === q.answer;
}).length;

Flex: NumericStepper rounding up decimal number

I have the following NumericStepper:
<s:NumericStepper id="estimertTidCell" value="{isNaN(hostComponent.estimertTid)?0:hostComponent.estimertTid}" stepSize="0.5" maximum="5" change="hostComponent.estimertTid=estimertTidCell.value"/>
When i set the value to e.g. 1.5 through the NumericStepper and store the value, the alert in the following code correctly displays 1.5:
private var _estimertTid:Number;
[Bindable]
public function get estimertTid():Number {
return _estimertTid;
}
public function set estimertTid(value:Number):void {
_estimertTid = value;
Alert.show("numeric stepper set:" + value);
invalidateSkinState();
}
Problem: My problem is that once the NumericStepper refreshes, or reloads the variable, it displays 2 instead of 1.5, or 4 instead of 3.5 etc. Anyone got any ideas of what is causing this behavior? I would think that by setting the stepSize=0.5 it would correctly display those decimal numbers.
Additional information: When i display the same variable in a spark Label, the value is correctly displayed as a decimal number.
At last! I found a small note that snapInterval has to be set to the same as stepSize or the value will be rounded up. So i hope my troubles help someone else in the future.
Set snapInterval to the same as stepSze to avoid numbers rounding up

How can I make the HTML5 number field display trailing zeroes?

I have a field:
<input type='number' />
I'd like to punch in 0.50 without it “correcting it” to 0.5, so it would display 0.50.
I attached an on('change') event to the input you want to have trailing 0's
$('.number-input').on('change', function(){
$(this).val(parseFloat($(this).val()).toFixed(2));
});
It just takes the value, casts it to a float, renders it to a string to the number of decimal places, and puts it back in as the value.
I've had a little play around with this and looked at the spec. It says that it must be a valid floating point number. There's one sentence in the definition of a valid floating point number it gives which caught my attention:
The best representation of the number n as a floating point number is
the string obtained from applying the JavaScript operator ToString to
n.
This means that the format will always be consistent with assessing what the number is, then using JavaScript's toString on that number. So no trailing 0s then.
So, you're going to have to resort to JavaScript. This isn't straightforward because document.getElementById('numInput').value = '0.50'; still gets corrected to 0.5, so the validation isn't triggered at onchange where the default action can be prevented, it's triggered internally.
This is the best solution I could come up with... it's a bit of a hack, and will need a bit of tweaking for robustness, but hopefully it'll do what you want:
var numInput = document.getElementById('numInput');
numInput.addEventListener('keypress', function () {
this.setAttribute('type', 'text');
});
numInput.addEventListener('click', function () {
this.setAttribute('type', 'number');
});
So if the user wants to enter the number by typing, it switches the input type to text, but when they click it, it converts it back to a number.
If you always want the trailing 0s no matter what the user types, then you could do it something like this:
var numInput = document.getElementById('numInput');
numInput.addEventListener('blur', function () {
if (this.value === '') {
return;
}
this.setAttribute('type', 'text');
if (this.value.indexOf('.') === -1) {
this.value = this.value + '.00';
}
while (this.value.indexOf('.') > this.value.length - 3) {
this.value = this.value + '0';
}
});
numInput.addEventListener('focus', function () {
this.setAttribute('type', 'number');
});
Edit: I think the second solution is more inline with what the user might expect, but it means that if the user types 0.5 it will be coerced to 0.50, so it depends if that's what you want.