PrimeReact number input field with react-hook-form - reactjs

I currently have the following code in my application:
<Controller defaultValue={0.0} name={"create_lng"}
control={createControl} rules={{
required: {value: true, message: t("pleaseSiteLng")}
}}
render={({field, fieldState}) => (
<InputNumber minFractionDigits={2}
mode={"decimal"}
className={classNames(
{"p-invalid": createErrors.create_lng})}
id={field.name} {...field} />
)}/>
Here I would now expect to be able to enter decimal numbers as defined.
But as soon as I enter anything in the input field, even numbers, the value changes to NaN and is displayed.
This only occurs with the InputNumber component, all other components can be filled without problems and also display the desired values.
I have seen that there is a property from react-hook-form "valueAsNumber", but I haven't found anything how to use it in a controller.
Does anyone here know how I can solve the problem?Does anyone here know how I can solve the problem?

I have updated this ticket: https://github.com/primefaces/primereact/issues/2547
I think there needs to be showcase example on how to use InputNumber with React Hook Forms if it is not straightforward like you are seeing.
Edit 07/30/2022:
I have it working and here are two examples:
Price example must be between 0 and 250,000.
<Controller name="price" control={control} rules={{ required: 'Price is required.', min: 0, max: 250000 }} render={({ field, fieldState }) => (
<>
<label htmlFor={field.name} className={classNames({ 'p-error': errors.price })}>Price*</label>
<InputNumber id={field.name} ref={field.ref} value={field.value} onBlur={field.onBlur} onValueChange={(e) => field.onChange(e)} mode="currency" currency="USD" locale="en-US" inputClassName={classNames({ 'p-invalid': fieldState.error })} />
{getFormErrorMessage(fieldState)}
</>
)} />
Year example must be between 1970-2030:
<Controller name="year" control={control} rules={{ required: 'Year is required.', min: 1970, max: 2050 }} render={({ field, fieldState }) => (
<>
<label htmlFor={field.name} className={classNames({ 'p-error': errors.year })}>Year*</label>
<InputNumber id={field.name} ref={field.ref} value={field.value} onBlur={field.onBlur} onValueChange={(e) => field.onChange(e)} useGrouping={false} inputClassName={classNames({ 'p-invalid': fieldState.error })} />
{getFormErrorMessage(fieldState)}
</>
)} />

I encountered the same issue when working with Primereact InputNumber and Formik.
In my case the reason for unexpected behavior of InputNumber component is primereact custom onChange method of InputNumber:
onChange?(e: InputNumberChangeParams): void
interface InputNumberChangeParams {
originalEvent: React.SyntheticEvent;
value: number | null;
}
(Compared to InputText onChange property: onChange?: ChangeEventHandler<HTMLInputElement> | undefined )
formik.handleChange(e) expects e: React.ChangeEvent<any> which means I can't pass (e: InputNumberChangeParams) to the function. The solution for me was to use another function to handle the change and work only with e.value which is provided by InputNumber onChange method.
onChange={(e): void => {
formik.setFieldValue("fieldName", e.value);
}}

Related

React Hook Form - Rules.Validate not triggered

I have the following field with a RHF controller and a MUI Textfield:
<Controller
control={control}
name="name"
defaultValue=""
rules={{
required: true,
minLength: 3,
maxLength: 300,
validate: wtf,
}}
render={({ field, fieldState: { error } }) => (
<TextField
{...field}
fullWidth
label="Name"
size="small"
helperText={formState?.errors?.name?.message}
error={error !== undefined}
/>
)}
/>
The wtf method isn't getting called on input change. I've tried with different revalidate modes but this is just not firing at all. Am I missing something here? I've checked examples and tutorials and they all seem to do it this way.
I created a sandbox here and it works perfectly.
I only had to add {mode: "onChange"} inside the useForm method call and return a string if the validation function failed.
I based my improvements in the documentation for register options as it is recommended here in the rules section of the Controller component.
Feel free to ask for any further help.
-Ado

MUI TextField not accepting pattern

I want to make Material UI TextField to only accept number values, in the MUI documentation the solution is as following:
<TextField inputProps={{ inputMode: 'numeric', pattern: '[0-9]*' }} />
However this is for the version 5 of MUI, and in my case I'm using the version 4, so I tried as following:
<TextField
InputProps={{
inputProps: {
inputMode: "numeric",
pattern: "[0-9]*"
}
}}
/>
But for some reason, this is not working, and I can still type values other than numbers in the input.
Using input="number" is not an option, as I don't want to keep the 0 when the input is empty, neither I want the stepper to be visible.
Full example is in this sandbox: https://codesandbox.io/s/mui4-forked-0j98er?file=/src/App.js:136-408
How can I solve this ?
You can use controlled input and clean value from non-numeric characters
export default function MyComponent() {
const [inputValue, setInputValue] = useState("");
function onChange(e) {
setInputValue(e.target.value.replace(/\D/g, ""));
}
return (
<TextField
label="number input"
variant="outlined"
color="secondary"
fullWidth
value={inputValue}
onChange={onChange}
inputProps={{
maxLength: 5
}}
/>
);
}
I was inspired by #Dmitriif's answer because it uses less code. But I tweaked it a bit.
live demo
However, the min: 0 doesn't work
export default function IconButtons() {
return (
<TextField
label="number input"
variant="outlined"
color="secondary"
fullWidth
inputProps={{
type: "text",
inputMode: "numeric",
pattern: "d*",
min: 0,
maxLength: 5
}}
/>
);
}
TextField in mui v4 also has inputProps property which can help you to apply desired attributes to the input element. However, you will need to set another property type="number" to make the component work correctly in your case:
<TextField
label="number input"
variant="outlined"
color="secondary"
fullWidth
type="number"
inputProps={{
min: 0,
maxLength: 5,
inputMode: "numeric",
pattern: "[0-9]*"
}}
/>
Demo

Material UI Slider with React hook form

I'm having a hard time integrating material Ui Slider with React hook form. It's not registering the values. It's printing the value as undefined on console.log. Got an idea where I might be wrong?
<Controller
render={({ field: { value, onChange } }) => (
<CustomSlider
onChange={onChange}
value={value}
max={60}
marks={marks}
className={classes.slider}
defaultValue={10}
/>
)}
control={control}
name="slider"
/>
Here is codesandbox made by author of react-hook-form that contains many examples. Mui integration is also made.
According to example, you are supposted to do something like this:
<Controller
name="MUI_Slider"
control={control}
defaultValue={[0, 10]}
render={(props) => (
<Slider
{...props}
onChange={(_, value) => {
props.onChange(value);
}}
valueLabelDisplay="auto"
max={10}
step={1}
/>
)}
/>
Another question is in which part of your code are you trying to console.log() values.
You can use watch() method or
<button Click={() => console.log(getValues())}>get values</button>

Ant.design does not detect custom input component in Form.Item React

So, the main problem is, that antd form does not detect my custom input based on antd standard input:
There is a piece of form code (AddProduct):
<Form.Item
className="m-t-10"
name="price"
rules={[
{
required: true,
message: `${t('FORM.ERR.SHOP.PRICE')}`,
},
]}
>
<CurrencyInput size="small" placeholder={t('FORM.SHOP.PRICE_VAT')} name="price" />
</Form.Item>
There is my custom input (CurrencyInput):
return (
<Input size={props.size} placeholder={props.placeholder} name={props.name} type="number" prefix={settings[6].value} />
)
The problem is when I try to submit the form it does not detect currency input, throws err that this field is required. Any ideas are it possible to implement custom input, basically, it's more HOC than custom input
You need to pass to your custom component all props, because Form.Item pass to there onChange and value props
function CustomInput({size, placehodler, name, ...restProps}) {
return (
<Input size={size} placeholder={placeholder} name={name}
type="number" prefix={settings[6].value} {...restProps} />
)
}

How to make autocomplete field of material UI required?

I have tried a couple of ways in order to make the material UI's autocomplete field of type required but I am not getting the behavior that I wanted. I had encapsulated my field inside react hook form <Controller/> yet no luck. I want to trigger message 'Field is mandatory' on submit when nothing is added to the field.
Below is the code snippet, I have not removed comments so that it becomes a bit easier for others to understand the approach that I had followed earlier -
<Controller
name="displayName"
as={
<Autocomplete
value={lists}
multiple
fullWidth
size="small"
limitTags={1}
id="multiple-limit-lists"
options={moduleList}
getOptionLabel={(option) => option.displayName}
renderInput={(params,props) => {
return (
<div>
<div className="container">
<TextValidator {...params} variant="outlined" label="Display Name*" className="Display Text"
name="displayName" id="outlined-multiline-static"
placeholder="Enter Display-Name" size="small"
onChange={handleDisplay}
// validators={['required']} this and below line does throw a validation but the problem is this validation stays on the screen when user selects something in the autocomplete field which is wrong.
// errorMessages={['This field is required']}
// withRequiredValidator
/>
</div>
</div>
)
}}
/>
}
// onChange={handleDisplay}
control={control}
rules={{ required: true }}
// required
// defaultValue={options[0]}
/>
<ErrorMessage errors={errors} name="displayName" message="This is required" />
You can use the following logic to get it worked. Though this might not be the best solution but works.
<Autocomplete
renderInput={(params) => (
<TextField
{...params}
label={value.length === 0 ? title : title + " *"} //handle required mark(*) on label
required={value.length === 0}
/>
)}
/>
I tried using the built in required in textfield for autocomplete, and it works like a charm. Maybe you can use this as a reference.
<Autocomplete
renderInput={(params) => {
<TextField {...params} required />
}
// Other codes
/>
Since you are rendering <TextValidator>, you should apply mandatory(required) attribute to that component not on <AutomComplete>.
Try this if your Material UI version is v5
<TextField
{...params}
required
label="Tags"
value={value}
InputProps={{
...params.InputProps,
required: value.length === 0,
}}
/>

Resources