Send props to Number Format - Material UI TextField - reactjs

I want to create a TextField element to handle numerical fields. I want to handle this component dynamically, in this way it would help me not only to manage credit card formats, telephone etc. I am using the react-number-format library in the same way as the Material-UI example does.
I'm trying to send by props "prefix" and "format" without favorable result.
I wanted to know how I should send those properties, if I have a way to do it.
Thanks in advance !
function NumberFormatCustom(props) {
const { inputRef, onChange, ...other } = props;
return (
<NumberFormat
{...other}
getInputRef={inputRef}
onValueChange={values => {
onChange({
target: {
value: values.value
}
});
}}
thousandSeparator={","}
decimalSeparator={"."}
isNumericString
prefix={props.prefix} //"$"
/>
);
}
NumberFormatCustom.propTypes = {
inputRef: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired
};
class NumberTextField extends Component {
state = {
numberformat: this.props.value
};
handleChange = event => {
const targetField = this.props.name;
const targetValue = event.target.value;
this.setState({
...this.state,
numberformat: targetValue
});
this.props.updateCurrentUserFieldsOnChange(targetField, targetValue);
};
render() {
const { fullWidth, label, name, readOnly, prefix } = this.props;
return (
<Fragment>
<TextField
fullWidth={fullWidth ? fullWidth : true}
label={label ? label : "react-number-format"}
name={name}
value={this.state.numberformat}
onChange={this.handleChange}
InputProps={{
inputComponent: NumberFormatCustom,
readOnly: Boolean(readOnly),
prefix: prefix
}}
/>
</Fragment>
);
}
}

You must use the customInput props which will allow you to integrate the style of material-ui. You can also pass several props to be able to control as you wish the mask. Also if you want a prefix just use the prefix props. thousandSeparator is a boolean but by default the numbers are separated by commas, if you prefer spaces you just have to add it as in my example
import NumberFormat from 'react-number-format';
import TextField from 'material-ui/TextField';
<NumberFormat
{...props}
value={value}
name={name}
mask={mask}
customInput={TextField}
prefix={'$'}
format={format || null}
type="text"
thousandSeparator={thousandSeparator ? ' ' : null}
onValueChange={({ value: v }) => onChange({ target: { name, value: v } })}
/>

If you want to have format your textfield as numberformat you can add your number format filed to FormControl field of material ui like below code.
<FormControl focused className="col " variant="outlined">
<InputLabel className="mText">your label</InputLabel>
<NumberFormat customInput={TextField}
variant="outlined"
thousandSeparator={true}
onChange={handleChange}
autoComplete="off"/>
</CustomFormControl>
Best regards.

Related

How to display initialValues for material-ui autocomplete field?

I use the autocomplete field of material-ui (v5) and formik to generate my forms.
On this form, I have some lists defined as constants.
I use an api to get the default value of this list.
This api returns only the "code" of the option but not its label.
<Formik
enableReinitialize
initialValues={initialFormValues}
validationSchema={Yup.object().shape({
[...]
<Autocomplete
error={Boolean(touched.civility && errors.civility)}
helperText={touched.civility && errors.civility}
label="Civility"
margin="normal"
name="civility"
onBlur={handleBlur}
onChange={(e, value) => setFieldValue('civility', value)}
options={civilities}
value={values.civility}
getOptionLabel={(option) =>
option.name ? option.name : ''
}
isOptionEqualToValue={(option, value) => option.code === value}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label={<Trans>Civility</Trans>}
/>
)}
/>
My parameter isOptionEqualToValue is good because even if the value is not displayed in the input, it is well selected in the list.
You can see that the input text field is empty:
But if I unroll the list, I can see that my "ms" value has been selected:
What should I do to make the input text contain the default value?
After cloned your snippet code above, the problem was in getOptionLabel, the option argument is a string value, so it hasn't a name property and appears empty string. Here is an online example codesandbox.
import { useState } from "react";
import { Formik, Form } from "formik";
import Autocomplete from "#material-ui/lab/Autocomplete";
import TextField from "#material-ui/core/TextField";
export default function App() {
const civilities = ["Mr", "Ms", "Other"];
const [values, setValues] = useState({
civility: "Ms"
});
const handleBlur = (e) => {
console.log("Blur:", e.target.value);
};
const setFieldValue = (type, value) => {
setValues((oldValues) => ({ ...oldValues, [type]: value }));
};
return (
<Formik err>
{({ errors, touched }) => (
<Form>
<Autocomplete
error={Boolean(touched.civility && errors.civility)}
helperText={touched.civility && errors.civility}
label="Civility"
margin="normal"
name="civility"
onBlur={handleBlur}
onChange={(e, value) => setFieldValue("civility", value)}
options={civilities}
value={values.civility}
isOptionEqualToValue={(option, value) => option.code === value}
renderInput={(params) => (
<TextField {...params} variant="outlined" label="Civility" />
)}
/>
</Form>
)}
</Formik>
);
}

How to get the length of material UI input filed in React JS?

I am trying to enable the button when the material input text length is more than 0.
This is a material input field component.
const MaterialInput = ({
name,
id = name,
select,
options = [],
type,
label,
shrink,
formik,
disabled,
handleClick,
}) => {
return (
<TextField
type={type}
name={name}
label={label}
select={select}
autoComplete='off'
variant='outlined'
disabled={disabled}
className='material-input'
value={get(formik.values, name, '')}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
helperText={get(formik.touched, name) && get(formik.errors, name)}
error={get(formik.touched, name) && Boolean(get(formik.errors, name))}
InputLabelProps={{ shrink }}
>
{options.map((item) => (
<MenuItem
onClick={handleClick}
className='text-capitalize'
value={item.value}
key={item.value}
disabled={item.disabled || false}
>
{item.label}
</MenuItem>
))}
</TextField>
);
};
This is how I am using it in other components.
aComponent.js
<MaterialInput name={inputName} label={label} formik={typeofForm} />
How to Enable button when the length of text field is more than 0.
There are many other ways to achieve it.
For suppose you material input name is mobileNumber and formik submit logic in component.js you can write below snippet in ur aComponent.js
useEffect(() => {
if (formik.values.mobileNumber.length === 0) {
// do your disable logic here
} else {
// do your logic
}
}, [formik.values.mobileNumber]);
You must check your validation on textField onchange, in this case, that you use formik for your form, Formik main component has a a callback function validate, you can use this function to handle your validation,
example
<Formik
validate={(values) => { // here check your validations and you have access to the values }}
validateOnChange
validateOnBlur
/>
you can use what you pass to value prop in TextField and get its length
const getInputLength = (formik, name) => {
const value = get(formik.values, name, '');
return value.length;
}

Trying to use react-hook-form in combination with react-input mask

I have the following setup. My mask will show up, but when I type in it it just skips to the end of the line I am not quite sure what I am doing wrong here. I have tried putting all the props in the parent component and passing them all with a spread, That didn't work. I can provide more debugging if someone can give me an idea on where to debugg first and I'll do it.
Thanks ahead of time
import React from "react"
import { useForm } from "react-hook-form"
import MaskedInput from "react-input-mask"
const Quote = () => {
const { register, handleSubmit, watch, errors } = useForm();
const [tel, setTel] = React.useState("");
render(
<MaskedInput
mask="(780) 000-0000"
alwaysShowMask
onChange={(e) => setTel(e.target.value)}
value={tel}
name={data.title}
>
{(inputProps) => (
<input
ref={register({
required: true,
pattern: /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im,
})}
value={inputProps.tel}
name={inputProps.name}
{...inputProps}
/>
)}
</MaskedInput>
);
};
For those who are using react-hook-form version 7, here is an example how to get it to work:
<Controller
name="your input name"
control={control}
defaultValue=""
rules={{
required: true,
}}
render={({ field }) => (
<MaskedInput
mask="99/99"
maskChar=""
value={field.value}
onChange={field.onChange}
>
{(inputProps: any) => (
<input
{...inputProps}
type="text"
/>
)}
</MaskedInput>
)}
/>
This solution work for me.
You will need the following packages on your package.json:
"react-hook-form": "^7.34.0",
"#hookform/resolvers": "^2.9.7",
"react-input-mask": "^3.0.0-alpha.2",
"#types/react-input-mask": "^3.0.1",
You can install this version of react-input-mask with the comand ...
yarn add react-input-mask#next
yarn add #types/react-input-mask
Here is the code:
<InputMask
// mask options
mask={"99.999.999/9999-99"}
alwaysShowMask={false}
maskPlaceholder=''
// input options
type={'text'}
placeholder="Ex: 00.000.000/0000-00"
// react hook form register
{...register("cnpj", { required: true })}
/>
Mask format was wrong needed to be in something like this
mask="(+7 (999) 999-99-99)"
To help others
If you're using not controlled Input Fields (like native input), ou can use a function to mask the input
This cant be used with controlled input fields like (Material UI)
Example #component/input.tsx
import React from 'react'
import { Container, ErrorMessage } from './styles'
interface InputProps {
label?: string | true
defaultValue?: string
name?: string
type?: string
mask?: (value: string) => string
placeholder?: string
disabled?: boolean
error?: any
value?: string
register?: any
}
const Input: React.FC<InputProps> = ({
label,
defaultValue,
name,
type,
mask = (value: string) => value,
value,
placeholder,
disabled,
error,
register,
...inputProps
}) => {
return (
<Container>
{label && (
<label htmlFor={name}>
{(typeof label === 'string' && label) ||
placeholder ||
'Preencha este campo'}
</label>
)}
<input
onChange={e => (e.target.value = `${mask(e.target.value)}`)}
disabled={disabled}
ref={register}
id={name}
name={name}
type={type}
value={value}
placeholder={placeholder}
defaultValue={defaultValue}
{...inputProps}
/>
{error && <ErrorMessage>{error.message}</ErrorMessage>}
</Container>
)
}
export default Input
Usage example #page/form.tsx
function CPFMask(v: string): string {
v = v.replace(/\D/g, '')
v = v.replace(/^(\d{3})(\d)/g, '$1.$2')
v = v.replace(/^(\d{3})\.(\d{3})(\d)/, '$1.$2.$3')
v = v.replace(/^(\d{3})\.(\d{3})\.(\d{3})(\d)/, '$1.$2.$3-$4')
v = v.replace(/^(\d{3})\.(\d{3})\.(\d{3})\/(\d{2})(\d)/, '$1.$2.$3-$4')
return v.substring(0, 14)
}
...
<Input
type="text"
mask={CPFMask}
placeholder="CPF"
name="cpf"
label
register={register({
required: {
value: true,
message: 'CPF é obrigatório',
},
pattern: {
value: /([0-9]{2}[\.]?[0-9]{3}[\.]?[0-9]{3}[\/]?[0-9]{4}[-]?[0-9]{2})|([0-9]{3}[\.]?[0-9]{3}[\.]?[0-9]{3}[-]?[0-9]{2})/i,
message: 'CPF inválido',
},
})}
error={errors.cpf}
/>
...
Here's an example, wrap the children with a function, let's think of InputMask as Controller and children as render. So I put the ref prop on the children to redirect the ref errors directly to the children or render, not the Controller.
<InputMask name="npwp" mask="99.999.999.9-999.999">
{(inputProps) => (
<input
{...inputProps}
ref={register}
type="text"
placeholder="Input NPWP"
/>
)}
</InputMask>
This is how I did it without using register, and with a different masking library (s-yadav/react-number-format).
A work-around, as I could not get it to work with {...register()}.
<NumberFormat
type="tel"
defaultValue={formDefaultValue}
onValueChange={(values) => {
// Mirror the value for form validation
setFormValue("amount", values.value);
}}
customInput={(props) => {
return (
<Input
name="amount"
formatProps={props}
errors={formErrors}
/>
);
}}
/>
Inside Input component:
<input
{...(formatProps ? formatProps : {})}
/>

React phone input 2 with react hook form

I am using PhoneInput along with react hook form, I want to enable save button only if phone number is valid
Code:
<form onSubmit={handleSubmit(onSubmitRequest)}>
.....................other code..............
<Controller
as={
<PhoneInput
id="pNum"
placeholder="Enter phone number"
className={classes.phoneInput}
inputRef={register({required: true})}
isValid={(inputNumber, onlyCountries) => {
return onlyCountries.some((country) => {
return startsWith(inputNumber, country.dialCode) || startsWith(country.dialCode, inputNumber);
});
}}
/>
}
name="phoneNumber"
control={control}
/>
........................other code...................
<Button
fullWidth
type="submit"
variant="contained"
color={'primary'}
className={classes.submitBtn}
data-testid="customerFormButton"
disabled={!formState.isValid}
>
Save
</Button>
</form>
Here I used PhoneInput as controller along with isValid for it. How can I disable Save button for invalid phone number input?
How are you? I believe that your problem is because you are not configuring the rules for the controller.
You need to change your controller to something like this:
<Controller
as={
<PhoneInput
id="pNum"
placeholder="Enter phone number"
className={classes.phoneInput}
inputRef={register}
isValid={(inputNumber, onlyCountries) => {
return onlyCountries.some((country) => {
return startsWith(inputNumber, country.dialCode) || startsWith(country.dialCode, inputNumber);
});
}}
/>
}
name="phoneNumber"
control={control}
rules= {{required: true}}
/>
ref cannot be currently used on this element. react-phone-input-2.
Until its supported, you can provide a hidden input field which updates its value when the phone updates its value and put the ref on that
Example:
import React, { FC, useCallback } from 'react';
import { useFormContext } from 'react-hook-form';
import PhoneInput from 'react-phone-input-2';
import 'react-phone-input-2/lib/style.css';
interface Props {
handleChange: (name: string, val: string) => void;
defaultValue: string;
name: string;
}
const MyComponent: FC<Props> = ({ defaultValue, name, handleChange }) => {
const { register, setValue, watch } = useFormContext(); // Note: needs <FormProvider> in parent for this to be acessible
const nameHidden = `${name}Hidden`;
const handleChangePhone = useCallback(
(val: string) => {
setValue(nameHidden, val, { shouldValidate: true });
handleChange(name, val);
},
[handleChange]
);
return (
<>
<PhoneInput value={defaultValue as string} country="gb" onChange={handleChangePhone} />
<input
type="hidden"
name={nameHidden}
defaultValue={defaultValue}
ref={register({
// validate stuff here...
})}
/>
</>
);
};
export default MyComponent;

React - Date input value not updated to state

In my React App, I am able to set the state and update the database for all values except the date input field. My code is below:
import React, { Component } from 'react'
...
...
import DateInput from '../others/input/datePicker'
...
..
change = (what, e) => this.setState({ [what]: e.target.value })
changeDOB() {
this.setState({ age: document.getElementByClassNames("datePicker").value })
}
render() {
let {
...
...
age,
...
} = this.state
...
...
//date of birth
let stringAge = age.toString()
stringAge =
stringAge.substring(0, 4) +
'-' +
stringAge.substring(4, 6) +
'-' +
stringAge.substring(6, 8)
...
<DateInput
type="date"
change={this.changeDOB}
placeholder={stringAge}
className="datePicker"
/>
...
...
const mapStateToProps = store => ({
ud: store.User.user_details,
tags: store.User.tags,
session: store.User.session,
})
export default connect(mapStateToProps)(EditProfile)
export { EditProfile as PureEditProfile }
Here is DateInput code:
import React from 'react'
import { string, func, oneOf, bool } from 'prop-types'
const DateInput = ({ type, placeholder, ...props }) => (
<input
type={type}
placeholder={placeholder}
spellCheck="false"
autoComplete="false"
{...props}
/>
)
DateInput.defaultProps = {
type: 'date',
placeholder: '',
disabled: false,
}
DateInput.propTypes = {
type: oneOf(['date']),
placeholder: string.isRequired,
maxLength: string,
disabled: bool,
}
export default DateInput
I tried this.change like other fields but that does not work either.
How to get the new value updated in the state ?
Note: The text is red is the value currently in the database.
You need to add onChange attribute for the input field in the DateInput component as
const DateInput = ({ type, placeholder, ...props }) => (
<input
type={type}
placeholder={placeholder}
spellCheck="false"
autoComplete="false"
onChange = {props.Onchange}
{...props}
/>
)
Then your main component should be as
changeDOB(e) {
this.setState({ age: e.target.value });
}
render() {
return(
<DateInput
type="date"
Onchange={this.changeDOB}
placeholder={stringAge}
className="datePicker"
/>
)
}
Please find a working example here
You are passing all the props to input component but you need to pass your event handler function to onchange input element or Try onkeypress instead. Something like below. You can also try getting input value with event instead of document
Arrow function: No need of manual binding
changeDOB = (event) => {
this.setState({ age: event.target.value
})
}
<DateInput
type="date"
change={this.changeDOB}
placeholder={stringAge}
className="datePicker"
value={this.state.age}
/>
<input
type={type}
placeholder={placeholder}
spellCheck="false"
autoComplete="false"
onchange={(event) => props.change(event)}
value={props.value}
{...props}
/>
Normal function: Binding required and only in constructor
this.changeDOB = this.changeDOB.bind(this);
changeDOB(event){
this.setState({ age: event.target.value
})
}
<DateInput
type="date"
change={this.changeDOB}
placeholder={stringAge}
className="datePicker"
value={this.state.age}
/>
<input
type={type}
placeholder={placeholder}
spellCheck="false"
autoComplete="false"
onchange={props.change}
value={props.value}
{...props}
/>
The date is being taken in the ISO format whereas the display expects it in the localformat.
This worked for me:
const [deadline, setDeadline] = useState(new Date());
<input
type="date"
id="start"
name="trip-start"
value={deadline.toLocaleDateString}
onChange={(event) => setDeadline({deadline:event.target.value})}
min="2022-01-01"
max="2022-12-31"
>
</input>

Resources