I have a state as value: 10.00 and once I update it with some operation and add it to a <Text> the ".00" part gets trimmed off. If it was a value like 10.50, it'll be displayed as 10.5
This is a issue as I want to display currency values. How to handle this?
Found the answer. To have the value with decimal values, use toFixed() method.
Example:
var value = 10;
value = value.toFixed(2);
this.setState({subTotal: value});
The output would be: 10.00
here is another solution you can also try, what i need is don't allow to enter more than 2 decimal digits (after decimal point) and also shouldn't allow more than two decimal points or any other character.
ConTwoDecDigit=(digit)=>{
return digit.indexOf(".")>0?
digit.split(".").length>=2?
digit.split(".")[0]+"."+digit.split(".")[1].substring(-1,2)
: digit
: digit
}
<TextInput
value={this.state.salary}
onChangeText={value => this.setState({ salary: this.ConTwoDecDigit(value) })}
keyboardType={'decimal-pad'}
/>
An alternative to the verified answer which catches more edge cases and allows string inputs. (defaults to 2dp but can be set by function caller)
export function normaliseValue (value: string, decimals = 2) {
if (!value) {
return ''
}
if (value === '.') {
return value = '0.'
}
var regex = new RegExp(`^-?\\d+(?:\\.\\d{0,${decimals}})?`)
const decimalsNumber = value.toString().match(regex)[0]
const parsed = parseFloat(decimalsNumber).toFixed(2)
if (isNaN(parsed)) {
return '0'
}
return parsed
}
Example use in code:
<TextInput
label='Hours worked'
placeholder='Hours worked'
keyboardType='decimal-pad'
value={String(values.hours)}
onChangeText={(val) => setFieldValue('hours', normaliseValue(val, 3))}
/>
Related
This question already has answers here:
Remove/ truncate leading zeros by javascript/jquery
(17 answers)
Closed 19 days ago.
I am a beginner in React JS. I have a use case in that I want to correct the number that a user enters in <input type='number> field.
By default, a user can enter numbers with leading zeros like 0002 or -0042, etc.
I want to make it such that the leading zeros are removed when the user enters the number. Also, the user should be able to enter decimal as well as negative numbers. I have done it using onBlur but I want to somehow do it onChange method itself.
onChange=()=>{ ... }
<input type = 'number' onChange={onChange}>
I want to make it such that the leading zeros are removed when the user enters the number.
You can remove the leading zeros with String.replace:
// ... code that obtains the user input in `inputText` ...
inputSanitisedText = inputText.replace(/^0+/, '')
(I am assuming you don't want to change the user's input while they're entering it. That would be very bad UI design.)
You can use regex to remove zeros from beginning: /^0+/
In your case:
onChange = (e) => {
const _removedZeros = e.target.value.replace(/^0+/, '')
///...
}
you can simply multiplied value to 1, like this :
const [value, setValue] = useState("");
<input
value={Boolean(value) ? value : ''}
type="number"
onChange={(e) => setValue(e.target.value * 1)}
/>
in this way user cannot type leading zeros
As per your description, you can solve this by using the parseFloat() function. This function will remove the leading zeros and will convert the input value to a decimal/fractional number.
The code should be like this:
const onChange = (event) => {
const value = parseFloat(event.target.value);
event.target.value = isNaN(value) ? '' : value;
};
something like this?
foo.oninput = (e) => {
const value = foo.value;
let [_, sign, integer, decimals] = value.replace(/[^\d\.\-]/g, "") // invalid characters
.replace(/(\..*?)\./g, "$1") // multiple dots
.replace(/(.+)-/g, "$1") // invalid signs
.match(/^(-?)(.*?)((?:\.\d*)?)$/);
let pos = foo.selectionStart - 1;
if(!integer && decimals) pos += 2;
// don't convert an empty string into a 0,
// unless there are decimal places following
if(integer || decimals) {
integer = +integer;
}
const formatted = sign + integer + decimals;
if(formatted !== value) {
foo.value = formatted;
foo.setSelectionRange(pos, pos);
}
}
<input type="text" id="foo" />
I'm make a react application that takes an input that should be shown to two decimal places. When I have a number that has a 0 in the second decimal place it is removing it. I notice it does this as soon as the string gets converted to a number.
This is how the input looks when first loaded. The preferred display would be 1.60 (two decimal places)
function ProductMaterial() {
const [name, setName] = useState("");
function handleChange(e) {
let val = e.target.value;
//ignore values entered after 2 decimal places
if (val.includes(".") && val.split(".")[1].length > 2) {
e.preventDefault();
} else {
setName(parseFloat(val));
setMaterialState(parseFloat(val));
}
calcTotal();
setTotal(product.id, product.total);
}
function handlePress(e) {
let val = e.target.value;
//ignore values entered after 2 decimal places.
//For some reason it needs to be compared to 1 for it to work with 0s.
if (val.includes(".") && val.split(".")[1].length > 1) {
e.preventDefault();
} else {
setName(parseFloat(val));
setMaterialState(parseFloat(val));
}
calcTotal();
setTotal(product.id, product.total);
}
return (
<div className="input-group-sm col-xs-2 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}
onKeyPress={handlePress}
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. Product.material needs to be a number for this for other calculations that happen.
function setMaterialState(newMaterial) {
product.material = newMaterial;
}
useEffect(() => {
setMaterial(product.id, product.material);
}, [product.material]);
I'm struggling on the forcing it to be two decimal places when there is a 0 at the end. 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() [], 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). Right now value in the input value={product.material} needs to be a number to correctly execute calculations and collect user input. I'm not sure what the best solution for this is.
Using the Intl.NumberFormat object is one approach you could try.
function ExampleComponent() {
const [inputVal, setInputVal] = useState("0.00");
// you can `parseFloat` the string back into a number
// where needed (e.g., calculations)
function handleChange(e) {
let number = e.target.value;
setInputVal(number);
}
// applies formatting when input has lost focus
function formatValue(e) {
if (inputVal) {
const locale = "en-US";
setInputVal(
new Intl.NumberFormat(locale, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: false, // commas will break `parseFloat`
}).format(parseFloat(inputVal))
);
}
}
return (
<>
<p>${inputVal ? inputVal : "0.00"}</p>
<input
type="number"
min="0"
step="0.01"
onChange={handleChange}
onBlur={formatValue}
value={inputVal}
/>
</>
);
}
Link to a working example:
codesandbox.io/s/xenodochial-hypatia-omsmzi
I have a field that must have a minimum value of 100,000 which is validated using a minValue function. The figure in the field must be formatted to display with commas where necessary, e.g - '100,000'
Redux Field
const minSumValue = 100000;
<Field
name="buildings_sum_insured"
type="text"
format={formatNumberField}
placeholder="Minimum €100,000"
component={dgInput}
validate={minValue(`${minSumValue}`)}
/>
formatNumberField function
function formatNumberField(number) {
if (!number) return "";
const n = parseInt(number.replace(/\D/g, ""), 10);
return n.toLocaleString();
}
Validate function
export const minValue = min => value => value && value < min ? `Must be at least ${min}` : undefined
The format number function is working as expected, however it's causing the validate function not to work. The validation is functional without the format function present.
How can I validate the formatted number in the validate function? Do I need to parse the value and remove any special characters?
Any help would be great!
It does seem kind of silly to go from string to number to string to number again, so there might be some better way to do this. But the following should work. You could change the minValue function but instead I am leaving that untouched and converting the value to a number before we call it.
We remove all commas from the string and return its numeric value:
const toNumber = (value, fallback = undefined) => {
return value ? parseFloat(value.replaceAll(",", "")) : fallback;
}
We use an arrow function to access the string value and then call the minValue function with its numeric conversion:
<Field
...
validate={(value) => minValue(minSumValue)(toNumber(value, undefined))}
/>
I am using react 16.8.2 new hooks API. -Just for info-
My problem only involves JS.
I have two input fields. They take only numbers as inputs. If the user enters /\D+/ (non-digits), the field is set to ''(empty). If he enters 2.3393, the number should always be rounded to two decimal places 2.34
Field1: onChange formats the number to $ 32,233,233,322.24
Field2: onChange formats the number to 99%. Decimals places are simply truncated.
The Input field should be able to handle e.nativeEvent.inputType deleteContentBackward as well. Such that if the user is at $ 2 and deletes 2, Field1 becomes empty. Similarly for Field2. 1% on deleting % becomes empty.
So far I have this:
const handleInputChange = function (e) {
const val = e.target.value;
const formatValue = function () {
if (/.*\d?\.\d*/.test(val)) return val.replace(/(?<=\d?\.\d*)\..*/g, '');
return +val.replace(/\$\s?|(,+)|%/g, '');
};
if (formatValue()) {
if (fieldSuffix === 'Percentage') {
if (e.nativeEvent.inputType === 'deleteContentBackward') return setVal(`${formatValue()}%`.replace(/^\d%$|\d(?=%)/, ''));
return setVal(`${formatValue()}%`);
}
if (fieldSuffix === 'Dollars') return setVal(`$ ${formatValue()}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','));
return setVal(formatValue());
}
return setVal('');
};
return (
<input
value={val}
onChange={handleInputChange}
/>
)
It does not work well for when user enters single .. $ are prepend for every . keystroke. The case that when user enters /\D+/ is not handled. % Field2 decimal place truncation case is also not handled. I can think of other cases also that are not handled.
My code is getting complicated. This approach is not elegant. Please Help.
The following code works well.
const handleInputChange = function (e) {
const formatValue = function () {
// Remove non-digit, except '.' and remove everything beginning from second decimal '.'
return e.target.value.replace(/[^0-9.]|(?<=^[^.]*\.[^.]*)\..*/g, '');
};
if (formatValue()) {
if (fieldSuffix === 'Percentage') {
// Percentage decimal truncated
const truncatedPercentage = formatValue().replace(/\..*/, '');
if (e.nativeEvent.inputType === 'deleteContentBackward') return setVal(`${formatValue()}%`.replace(/^\d%$|(\d|\.)(?=%)/, ''));
return setVal(`${truncatedPercentage}%`);
}
if (fieldSuffix === 'Dollars') {
// Truncated to two decimal places, not rounded
const truncatedDollar = formatValue().replace(/(?<=\.\d{2}).*/, '');
// Format and insert ','
return setVal(`$ ${truncatedDollar}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','));
}
}
return setVal('');
};
I still feel that there is a lot of redundancy and unhandled cases in this code
I have the following code. I would like to have it such that if price_result equals an integer, let's say 10, then I would like to add two decimal places. So 10 would be 10.00.
Or if it equals 10.6 would be 10.60. Not sure how to do this.
price_result = parseFloat(test_var.split('$')[1].slice(0,-1));
You can use toFixed() to do that
var twoPlacedFloat = parseFloat(yourString).toFixed(2)
If you need performance (like in games):
Math.round(number * 100) / 100
It's about 100 times as fast as parseFloat(number.toFixed(2))
http://jsperf.com/parsefloat-tofixed-vs-math-round
When you use toFixed, it always returns the value as a string. This sometimes complicates the code. To avoid that, you can make an alternative method for Number.
Number.prototype.round = function(p) {
p = p || 10;
return parseFloat( this.toFixed(p) );
};
and use:
var n = 22 / 7; // 3.142857142857143
n.round(3); // 3.143
or simply:
(22/7).round(3); // 3.143
To return a number, add another layer of parentheses. Keeps it clean.
var twoPlacedFloat = parseFloat((10.02745).toFixed(2));
If your objective is to parse, and your input might be a literal, then you'd expect a float and toFixed won't provide that, so here are two simple functions to provide this:
function parseFloat2Decimals(value) {
return parseFloat(parseFloat(value).toFixed(2));
}
function parseFloat2Decimals(value,decimalPlaces) {
return parseFloat(parseFloat(value).toFixed(decimalPlaces));
}
ceil from lodash is probably the best
_.ceil("315.9250488",2)
_.ceil(315.9250488,2)
_.ceil(undefined,2)
_.ceil(null,2)
_.ceil("",2)
will work also with a number and it's safe
You can use .toFixed() to for float value 2 digits
Exampale
let newValue = parseFloat(9.990000).toFixed(2)
//output
9.99
I have tried this for my case and it'll work fine.
var multiplied_value = parseFloat(given_quantity*given_price).toFixed(3);
Sample output:
9.007
parseFloat(parseFloat(amount).toFixed(2))
You have to parse it twice. The first time is to convert the string to a float, then fix it to two decimals (but the toFixed returns a string), and finally parse it again.
Please use below function if you don't want to round off.
function ConvertToDecimal(num) {
num = num.toString(); //If it's not already a String
num = num.slice(0, (num.indexOf(".")) + 3); //With 3 exposing the hundredths place
alert('M : ' + Number(num)); //If you need it back as a Number
}
For what its worth: A decimal number, is a decimal number, you either round it to some other value or not. Internally, it will approximate a decimal fraction according to the rule of floating point arthmetic and handling. It stays a decimal number (floating point, in JS a double) internally, no matter how you many digits you want to display it with.
To present it for display, you can choose the precision of the display to whatever you want by string conversion. Presentation is a display issue, not a storage thing.
#sd
Short Answer: There is no way in JS to have Number datatype value with trailing zeros after a decimal.
Long Answer: Its the property of toFixed or toPrecision function of JavaScript, to return the String. The reason for this is that the Number datatype cannot have value like a = 2.00, it will always remove the trailing zeros after the decimal, This is the inbuilt property of Number Datatype. So to achieve the above in JS we have 2 options
Either use data as a string or
Agree to have truncated value with case '0' at the end ex 2.50 -> 2.5.
You can store your price as a string
You can use
Number(string)
for your calculations.
example
Number("34.50") == 34.5
also
Number("35.65") == 35.65
If you're comfortable with the Number function , you can go with it.
Try this (see comments in code):
function fixInteger(el) {
// this is element's value selector, you should use your own
value = $(el).val();
if (value == '') {
value = 0;
}
newValue = parseInt(value);
// if new value is Nan (when input is a string with no integers in it)
if (isNaN(newValue)) {
value = 0;
newValue = parseInt(value);
}
// apply new value to element
$(el).val(newValue);
}
function fixPrice(el) {
// this is element's value selector, you should use your own
value = $(el).val();
if (value == '') {
value = 0;
}
newValue = parseFloat(value.replace(',', '.')).toFixed(2);
// if new value is Nan (when input is a string with no integers in it)
if (isNaN(newValue)) {
value = 0;
newValue = parseFloat(value).toFixed(2);
}
// apply new value to element
$(el).val(newValue);
}
Solution for FormArray controllers
Initialize FormArray form Builder
formInitilize() {
this.Form = this._formBuilder.group({
formArray: this._formBuilder.array([this.createForm()])
});
}
Create Form
createForm() {
return (this.Form = this._formBuilder.group({
convertodecimal: ['']
}));
}
Set Form Values into Form Controller
setFormvalues() {
this.Form.setControl('formArray', this._formBuilder.array([]));
const control = <FormArray>this.resourceBalanceForm.controls['formArray'];
this.ListArrayValues.forEach((x) => {
control.push(this.buildForm(x));
});
}
private buildForm(x): FormGroup {
const bindvalues= this._formBuilder.group({
convertodecimal: x.ArrayCollection1? parseFloat(x.ArrayCollection1[0].name).toFixed(2) : '' // Option for array collection
// convertodecimal: x.number.toFixed(2) --- option for two decimal value
});
return bindvalues;
}
I've got other solution.
You can use round() to do that instead toFixed()
var twoPlacedFloat = parseFloat(yourString).round(2)
The solution that work for me is the following
parseFloat(value)