Access input name attribute using template literals (React) - javascript

I'm having some problem to use errors (object provided by React Hook Form) to show a validation message (when a required input field is empty) for the input fields.
{
inputs.map((name) => (
<div key={name}>
<div style={{ marginTop: "3px" }}>
<input
name={`Chamfer Set.${name}`}
ref={register({ required: true })}
/>
</div>
{errors[`Chamfer Set.${name}`] && (
<span>Please enter a value for {name}.</span>
)}
</div>
));
}
Basically I have to link errors to the input name attribute, which in this case I'm using template literals. But it is not working and I guess it is something related to the literals, but I'm not very into it. Do you guys have any idea?
If I use console.log(errors), I have the following structure:

To access the error property just replace:
errors[`Chamfer Set.${name}`]
By:
errors["Chamfer Set"] && errors["Chamfer Set"][`${name}`]

As #Micheal mentioned, Try
{
inputs.map((name) => (
<div key={name}>
<div style={{ marginTop: "3px" }}>
<input
name={`Chamfer Set.${name}`}
ref={register({ required: true })}
/>
</div>
{errors[`Chamfer Set`][`${name}`] && (
<span>Please enter a value for {name}.</span>
)}
</div>
));
}

Related

Remove special character from input form

I'm new to JavaScript and already go through similar questions. I create an input mask for phone number where it has the region code in the form. However, it saves in the database together with the region code. How can I remove special character from an input form? For example:
(+60)123456789 --> 0123456789
I have this block of code where the input form goes:
<div className="mt-4">
<InputMaskComponent
id="company_phone_no"
mask="+(60)999999999999"
label="Company Phone Number"
value={(formState.company_phone_no || '').replace(/[^a-zA-Z0-9 ]/g, '')}
variant="standard"
onChange={handleChange}
>
</InputMaskComponent>
</div>
I try to use the replace() but still cannot managed to get it right. I already have this mask.js component:
export default function InputMaskComponent({id,mask,value,onChange,label,variant}) {
return (
<InputMask
id={id}
name={id}
mask={mask}
maskChar=""
value={value}
onChange={onChange}
>
{()=> <TextField fullWidth id={id} name={id} label={label} variant={variant} />}
</InputMask>
);
}

What is the difference between these two jsx syntax in React?

I have this form that draws some fields dynamically and set the state of the form, the code is working flawlessly check the code here,
this is how the return statement looks like
return (
<div className="patientForm">
<section className="form">
<form onSubmit={onSubmit}>
{patientFormFields.map(({ title, type, values }, index) => (
<div className="form-group w-50" key={index}>
<label htmlFor={title}>{capitalize(title)}</label>
{type === "select" ? (
<select
name={title}
id={title}
value={formValue[title]}
onChange={(e) => handleChange(e, title)}
>
{values.map(({ value, text }, index) => (
<option key={index} value={value}>
{text}
</option>
))}
</select>
) : (
<input
type={type}
name={title}
id={title}
placeholder={capitalize(title)}
value={formValue[title]}
onChange={(e) => handleChange(e, title)}
/>
)}
</div>
))}
</form>
</section>
</div>
);
My problem is I don't like having my return statement be filled with logic so I tried to refactor the logic part into a separate component, check the code here,
this is my return statement now
return (
<div className="patientForm">
<section className="form">
<form onSubmit={onSubmit}>
{patientFormFields.map(({ title, type, values }, index) => (
<div className="form-group w-50" key={index}>
<label htmlFor={title}>{capitalize(title)}</label>
<FormFields type={type} title={title} values={values} />
</div>
))}
</form>
</section>
</div>
);
but it didn't work as expected, the input kept losing focus when I type, I looked up the problem and it seems that when I update my state, the component rerenders, react can't compare the two components/functions and that causes a creating a new component, I guess?! when I tried using a regular function (line-53) that returns JSX it worked correctly, check the code here.
this is my return statement now
return (
<div className="patientForm">
<section className="form">
<form onSubmit={onSubmit}>
{patientFormFields.map(({ title, type, values }, index) => (
<div className="form-group w-50" key={index}>
<label htmlFor={title}>{capitalize(title)}</label>
{FormFields(type, title, values)}
</div>
))}
</form>
</section>
</div>
);
};
The explanation sort of make sense but I don't understand why did my code work when I converted the JSX call in the component return to a regular function, aren't they both functions? and shouldn't my second code also be buggy since functions are reference type, and react shouldn't be able to compare them, I feel there is something missing that I don't understand. Also, what would be a better way to refactor that logic away from the component return?
edit: add code snippet to the problem

Conditionally making a number field required with YUP in React with react-hook-form

I have a form where if a certain radio option is selected I want to render a number input box, which should be required if the radio option is selected.
Here is my YUP schema for the relevant inputs:
areSeatsLimited: yup.string().required(),
numOfSeats: yup
.number()
.positive("This field must contain a positive number")
.integer("This field should contain an integer")
.when("areSeatsLimited", {
is: "yes",
then: yup.number().required().typeError("The field must contain a number"),
otherwise: yup.number().notRequired().typeError("The field must contain a number"),
}),
This works in a sense that if I open the form for the first time and dont choose the option that seats are limited, upon submitting I get no error from the numOfSeats input, which is expected since its not required.
However, if I check the seats are limited, then it gives me the error, which is also expected since its now required. But here is the problem: when I check that seats are unlimited again after selecting that they are limited. It still throws me the error as if the field is required. Also note its throwing me the typerror message("The field must contain a number")
Here is the react code for that part of the form
<div className="radio" style={{ display: "block", marginTop: "10px" }}>
<input
value="no"
type="radio"
id="unlimited"
{...register("areSeatsLimited")}
checked={areSeatsLimited === "no" || areSeatsLimited === undefined ? true : false}
/>
<label htmlFor="unlimited">Unlimited</label>
</div>
<div className="radio" style={{ display: "block", marginTop: "10px" }}>
<input
value="yes"
type="radio"
id="limited"
{...register("areSeatsLimited")}
checked={areSeatsLimited === "yes" ? true : false}
/>
<label htmlFor="limited">Limited</label>
</div>
{areSeatsLimited === "yes" ? (
<div className={`form-group required ${errors?.numOfSeats?.message ? "has-error" : ""}`}>
<label htmlFor="numOfSeats">Number Of Seats</label>
<input
type="number"
id="numOfSeats"
className="form-control"
placeholder="Amount of available seats..."
{...register("numOfSeats")}
/>
{errors?.numOfSeats?.message ? (
<span style={{ color: "var(--input-error-text-color)" }}>{errors.numOfSeats.message}</span>
) : (
""
)}
</div>
I have a similar set of fields but with string, and it works as expected.
This should solve your problem:
otherwise: number().transform(() => {
return undefined;
}).nullable().notRequired(),
An input field, according to this guy here, will return an empty string if you don't provide input, that is why it is throwing typeError. So you need to manually force it to return undefined when the seats are unlimited.

Form.Provider does not work properly saving data

I use in my react application Form.Provider from Ant Design library.I use it, because i generate inner forms inside main form. My target is, when i will click on SUBMIT button, to output in the same time data from outer form and from inner forms. The number of inner forms depend by user, so he can generate as many he wants.
My outer form:
<Form.Provider
onFormFinish={(name, { values, forms }) => {
console.log(forms);
if (name === "inner") {
const { inner } = forms;
const innerF = inner.getFieldValue("first") || [];
console.log(innerF);
}
}}
>
<Form name="outter" onFinish={onFinish}>
{nr.map(i => (
<div>
<p key={i}>{i}</p>
<Inner nr={i} />
</div>
))}
<Form.Item name="nr" label="Nr">
<InputNumber min={1} max={5} onChange={handleInputChange} />;
</Form.Item>
<Form.Item>
<Button htmlType="submit" type="primary">
Submit
</Button>
</Form.Item>
</Form>
</Form.Provider>
And my inner form
<Form name="inner" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{fields.map(field => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "last"]}
fieldKey={[field.fieldKey, "last"]}
rules={[{ required: true, message: "Missing last name" }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button ref={myRef} type="primary" htmlType="submit">
-
</Button>
</Form.Item>
</Form>
I added Form.Provider in outer form but i can't figure out how to get all values from inner form and from outer form in the same thime, clicking on SUBMIT button.
Demo: https://codesandbox.io/s/mutable-tree-5c1y1?file=/Outer.js:485-1291
Question: Why Form.Provider does not work and how to output all data, from inner, and from outer form, clicking on the SUBMIT button?
From what I can understand after reading a little bit of the documentation of Ant Design, in order for the outer onFinish to run, one of the forms need to submit. This can either be achieved by holding the reference of the form from the outer component and calling submit(), or declaring the button as a children of the Form.Item. In the second case, you would get a button after every form. Which is something you don't want. I would say go for the solution I have provided in here after the "Edit" part.

Validate redux form input field when user is entering

Is there any way to validate an input field in the redux form when the user enters a value? All my validation are working when I click out or press the submit button. I want my validations to run when the user is typing. My code for the field is this:
<div className="fieldRow noMargin">
<Field
id={FIRST_TIME_OTP_NUMBER}
name={FIRST_TIME_OTP_NUMBER}
component={InputTextField}
className="text xxs"
classNameInvalid="text xxs error"
/>
</div>
You can check if field is active from metadata(props under the meta key), and show error if its value is true.
const renderField = ({input, label, type, meta: {active, touched, error, warning}}) => (
<div>
<label>{label}</label>
<div>
<input
{...input}
placeholder={label}
type={type}
onFocus={input.onFocus}
/>
{(active || touched) &&
((error && <span>{error}</span>) ||
(warning && <span>{warning}</span>))}
</div>
</div>
);
NOTE: It will only work if you are passing onFocus to your input element.
Codesandbox demo

Categories