In formik, shorthand input field is not working - javascript

In Formik, I try to use {...formik.getFieldProps('email')} on my input field
instead of using value, onChange, and onBlur. But it's not working.
This works :
<input id="email" name="email" type="text" value={formik.values.email} onChange={formik.handleChange} onBlur={formik.handleBlur} />
But this doesn't :
<input id="email" type="text" {...formik.getFieldProps("email")} />
Code is here : https://codesandbox.io/s/formik-pb-with-getfieldprops-83tze?fontsize=14
Any ideas ?
Thanks !

As MiDas said, what you are doing should work if you are on latest version.
I'll mention an even more concise alternative: the Field component.
Field component handles the field property propagation for you.
Simple example:
<Field name="email" type="text" />
Notice that you don't need to do {...formik.getFieldProps("email")}, or any other "boilerplate".
Related to Field is useField, which can be used to make custom form components just as easy to use - no "boilerplate" needed.
A custom component:
function TextInputWithLabel({ label, ...props }) {
// useField() returns [formik.getFieldProps(), formik.getFieldMeta()]
// which we can spread on <input> and also replace ErrorMessage entirely.
const [field, meta] = useField(props);
return (
<>
<label
htmlFor={props.id || props.name}
css={{ backgroundColor: props.backgroundColor }}
>
{label}
</label>
<input className="text-input" {...field} type="text" {...props} />
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
}
Usage:
<TextInputWithLabel name="input1" label="Random comment" />
As seen in code from codesandbox.

Related

How to validate select input field using React Hook Form?

I'm trying to validate a form made using form fields from React MD by using React Hook Form. The text input fields are working fine.
But the validations aren't working on the select field. Here is the code:
<Controller
control={control}
name="salutation"
defaultValue=""
rules={{ required: "Salutation is required" }}
disabled={isSubmitting}
render={(props) => (
<Select
id="salutation"
{...props}
label="Salutation"
options={SALUTATION_ITEMS}
value={salutationValue}
onChange={(e) => handleSalutationChange(e)}
disableLeftAddon={false}
rightChildren={
<RiArrowDownSFill className="dropDownArrow" />
}
/>
);
}}
/>
The error persists even after the user selects a value:
{errors.salutation && (
<div className="validation-alert msg-error ">
<p>{errors.salutation.message}</p>
</div>
)}
I'm probably missing something or doing something wrong.
I think you are missing props.value and props.onChange(e) and you may not need handleSalutationChange(e):
<Controller
control={control}
name="salutation"
defaultValue=""
rules={{ required: "Salutation is required" }}
disabled={isSubmitting}
render={(props) => (
<Select
id="salutation"
{...props}
label="Salutation"
options={SALUTATION_ITEMS}
value={props.value} // This one: props.value
onChange={(e) => {
props.onChange(e) // I think you are missing this
handleSalutationChange(e) // NOT Needed ?
}}
disableLeftAddon={false}
rightChildren={
<RiArrowDownSFill className="dropDownArrow" />
}
/>
);
}}
/>

How can Prompt be used to stop navigation on formik dirty state

I'm using formik in a reactjs project, and I want to use Prompt from react-router to open a notification before a user leaves and loses changes made to their submission.
I'd expected something like this to work:
<Prompt
when={formik.dirty}
message="You have unsaved changes. Are you sure you want to leave?"
/>
My formik block looks like this:
const formik = useFormik({
initialValues: {
<values>
},
enableReinitialize: true,
validate,
onSubmit: values => {
<submit functional stuff>
}
});
And my form is something like this:
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
disabled={isDisabled}
/>
{formik.errors.name ? <div className="text-danger">{formik.errors.name}</div> : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
disabled={isDisabled}
/>
{formik.errors.subject ? <div className="text-danger">{formik.errors.subject}</div> : null}
</div>
</div>
</form>
but it appears that formik.dirty is either not defined or it's not seen as true (despite making changes to the form).
How would I properly use the dirty prop to trigger the Prompt?
I am not sure what kind of setup you have, but I created a PoC with routing which has two tabs (links) for navigation and I am using prompt on tab with formik form component.
import React from "react";
import { Prompt } from "react-router-dom";
import { useFormik } from "formik";
const MyForm = () => {
const formik = useFormik({
initialValues: {
name: "",
subject: ""
},
enableReinitialize: true,
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
},
onChange: (e) => {
console.log(e);
}
});
return (
<div>
<Prompt
when={!!formik.dirty}
message={(location) =>
`Are you sure you want to go to ${location.pathname}`
}
/>
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
/>
{formik.errors.name ? (
<div className="text-danger">{formik.errors.name}</div>
) : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
/>
{formik.errors.subject ? (
<div className="text-danger">{formik.errors.subject}</div>
) : null}
</div>
{formik.dirty && <button tye="submit">Save</button>}
</div>
</form>
</div>
);
};
export default MyForm;
take a look the this codesandbox.

submit form in react with variable set of inputs?

//form.js
const form=({seg,onSubmit,onChange})=>{
<form id="input_form" onSubmit={onSubmit}>
{seg.map(item=>
<>
<input type="number" onChange={onChange} name={item}
type="number" required className="form-control control" id={item+1} />
</>
)}
</form>}
// app.js
state={array:[0.0,1.0], seg=3}
onSubmit=()=>{
}
onChange=()=>{
// should update the array(as this.state.array=[0.0, inputa,inputb,inputc..., 1.0])}
how do i update the array? what should be the onChange, onSubmit func?
(this.state.seg is variable).
You can use this.setState({ seg : newValue});
You can set the seg state property value like this
onChange=(event)=>{
this.setState({seg: event.target.value});
}
I suppose yo need to update the array with current seg value. If you want to do it using onSubmit, you have to use a button with type of submit. Then the button calls the onSubmit function. Try this.
<form id="input_form" onSubmit={onSubmit}>
{seg.map(item =>
<>
<input type="number" onChange={onChange} name={item} type="number" required className="form-control control" id={item + 1} />
<button type="submit">Submit</button>
</>
)}
</form>
onSubmit=()=>{
this.setState({
array: [...this.state.array, seg]
})
}

Display emoji object to text field or textarea in React

I'm building my app with emoji-mart lib.
I have text input like this:
<FormGroup>
{emoji}
<EmojiMartPicker
set='emojione'
onSelect={(emoji) => console.log(emoji)}
onChange={this.onChange}
>
<Input
type="text"
name="emotion"
bsSize="sm"
autoComplete="off"
value={report.emotion.colons}
onChange={this.onHandleFormChange}
required
/>
</EmojiMartPicker>
</FormGroup>
Now, I wanna display Emoji Object to text input. In value attribute. I want to display emoji, not value text.
How can we do that?
See my detail problem:
https://codesandbox.io/s/646xom9y1z
Sorry, I found that just only add native props to solved my problem.
Like this:
value={report.emotion.native}
That's it..
I solve this problem take a look.
<div className="chatemoji">
<ButtonToolbar >
<div onClick={e => e.preventDefault()}>
{/* <EmojiField
name="textarea"
onChange={this.onChange.bind(this)}
fieldType="input"
/> */}
<EmojiField name="my-textarea" onChange={this.onChange.bind(this)} fieldType="input" />
</div>
</ButtonToolbar>
</div>
In onChange you have to call this code
onChange(e,value) {
this.state.data += value;
this.setState({ data: this.state.data });
}

Put length constraint in a TextField in react js

I am taking an input from the user of the card number and wants that the length entered by user must not be less than and more than 12. Here is the declaration of my textfield.
<TextField
id="SigninTextfield"
label="Aadhaar number"
id="Aadhar"
lineDirection="center"
required={true}
type="number"
maxLength={12}
style={styles.rootstyle}
erorText="Please enter only 12 digits number"
/>
Now I am not understanding whether to use javascript or any event handler for restricting the length.
You can set the maxLength property for limiting the text in text box.
Instead of onChange method you can pass maxLength to the inputProps (lowercase i, not InputProps) props of material-ui TextField.
<TextField
required
id="required"
label="Required"
defaultValue="Hello World"
inputProps={{ maxLength: 12 }}
/>
Basically we can edit all input element's native attrs via inputProps object.
Link to TextField Api
I found another solution here.
<TextField
required
id="required"
label="Required"
defaultValue="Hello World"
onInput = {(e) =>{
e.target.value = Math.max(0, parseInt(e.target.value) ).toString().slice(0,12)
}}/>
<TextField
autoFocus={true}
name="name"
onChange={handleChange}
placeholder="placeholder"
id="filled-basic"
variant="filled"
size="small"
fullWidth
inputProps={{
maxLength: 25,
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
<TextField
id="username"
name="username"
label={translate('username')}
onChange={handleChange}
onBlur={handleBlur}
value={values.username}
error={Boolean(errors.username) && touched.username}
helperText={(errors.username && touched.username && translate(errors.username))}
required
inputProps={{maxLength :20}}
/>
Is it worth noting that Material-ui <TextField /> component doesn not have a maxlength property. However, the original html <input> does. So you don't really need to create any extra function to get this to work. Just use the base <input> attributes using inputProps.
The actual answer is this:
inputProps={
{maxLength: 22}
}
// Result => <input maxlength="yourvalue" />
What this does is it sets the maxlength attribute of the underlying <input> resulting in: <input maxlength="yourvalue" />. Another important thing to note here is that you use inputProps and not InputProps. The one you're targeting is the small letter inputProps.
I discovered something weird on the behavior between TextField and Input from Material UI
They are very similar to each other, the problem I see is the following:
On the TextField component, if you use InputProps with capital "I", the Adorments are shown, but in the other hand if you use it as lowercase "inputProps", the maxLength Html attribute is TAKEN as valid, but not the adorments
I ended up using INPUT instead of a TextField so you can use adorments in
<TextField
variant="outlined"
required
fullWidth
error={errors.email}
id="email"
label={t("signup-page.label-email")}
name="email"
onChange={handleChange}
inputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton aria-label="toggle password visibility">
<EmailIcon />
</IconButton>
</InputAdornment>
),
maxLength: 120,
}}
/>
in the above code the adorment is ignored, but maxLength taken used as "inputProps" camel case
The below code is a working example, as you might see, I embraced it as in the old style within a Form Control, the input label and the input "outlinedInput"
<FormControl variant="outlined" fullWidth>
<InputLabel htmlFor="firstName">Password</InputLabel>
<OutlinedInput
value={values.firstName}
autoComplete="outlined"
name="firstName"
variant="outlined"
required
fullWidth
error={errors.firstName}
id="firstName"
label={t("signup-page.label-firstname")}
onChange={handleChange}
autoFocus
inputProps={{ maxLength: 32 }}
/>
</FormControl>
Solution. My final recommendation, use an OutlinedInput instead of a TextField, so you can put in a separated way Adorments and also maxLength
Below a working example with FormControl OutlinedInput, inputProps - maxLength and an end Adorment Icon
<FormControl variant="outlined" fullWidth>
<InputLabel htmlFor="password">Password</InputLabel>
<OutlinedInput
value={values.passwordConfirm}
variant="outlined"
required
fullWidth
error={errors.passwordConfirm}
name="passwordConfirm"
label={t("signup-page.label-password-confirm")}
type={values.showPasswordConfirm ? "text" : "password"}
id="password-confirm"
onChange={handleChange}
inputProps= {{maxLength:32}}
endAdornment= {
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword("passwordConfirm")}
onMouseDown={handleMouseDownPassword}
>
{values.showPasswordConfirm ? (
<Visibility />
) : (
<VisibilityOff />
)}
</IconButton>
</InputAdornment>
}
/>
{errors.passwordConfirm && (
<p className="error"> {errors.passwordConfirm} </p>
)}
</FormControl>
If you're using MUI 5, version 5.0.6, due to a support for legacy, you will have to do something like this,
<TextField
id="standard-textarea"
label="A label"
placeholder="Some text here"
multiline
variant="standard"
defaultValue={"Hello"}
inputProps={{
maxLength: 255,
}}
InputProps={{
disableUnderline: true,
}}
/>
The TextField supports both inputProps and InputProps, but some properties don't work vice versa.
maxLength does not work as expected in InputProps, and a property like disableUnderline for the MUI 5 does not work as expected in inputProps.
Be careful with a possible typo with the i.
See the API for more information, https://mui.com/api/text-field/.
The accepted answer won't work in Firefox for large numbers (greater than 16 digits). Numbers just behaves in a weird way.
For a 22 length field we ended up using this:
<TextField
required
validateOnBlur
field="cbu"
label="22 dígitos del CBU"
validate={validate.required}
type="text"
inputProps={
{maxLength: 22}
}
onInput={(e) => { e.target.value = e.target.value.replace(/[^0-9]/g, '') }}
/>
Basically, native maxLength constraint for text fields, plus a conversion from string to numbers "on the fly".
Improvement
Also you may prefer to make this reusable and more semantic.
# constraints.js
const onlyNumbers = (e) => { e.target.value = e.target.value.replace(/[^0-9]/g, '') };
# form.js
<TextField
field="cbu"
label="22 dígitos del CBU"
inputProps={
{maxLength: 22}
}
onInput={(e) => onlyNumbers(e) }
The material-design <TextField /> component haven't any length property.
You can create yours in the onChange() method.
updateTextField(event,value){
if(value.length <= 12){
//Update your state
}
else{
//Value length is biggest than 12
}
}
<TextField
id="SigninTextfield"
label="Aadhaar number"
id="Aadhar"
lineDirection="center"
required={true}
type="number"
onChange={(e,v) => this.updateTextField(e,v)}
style={styles.rootstyle}
erorText="Please enter only 12 digits number"
/>
In your change handler, just write.
if (event.target.value.length !== maxLength ) return false;
I had a similar issue, but with TextareaAutosize. Unfortunately,
inputProps={{ maxLength: 12 }}
doesn't work with TextareaAutosize.
The below workaround works for TextareaAutosize and for both text and numbers.
Inspired by the accepted answer to this question - https://stackoverflow.com/a/49130234/5236534
onInput = {(e) =>{
e.target.value = (e.target.value).toString().slice(0,10)
import * as React from 'react';
import TextareaAutosize from '#mui/material/TextareaAutosize';
export default function MinHeightTextarea() {
return (
<TextareaAutosize
aria-label="minimum height"
minRows={3}
placeholder="Minimum 3 rows"
style={{ width: 200 }}
onInput = {(e) =>{
e.target.value = (e.target.value).toString().slice(0,10)
}}
/>
);
}
Link to demo: Limiting character length in MUI TextareaAutosize
<TextField
id="SigninTextfield"
label="Aadhaar number"
id="Aadhar"
lineDirection="center"
required={true}
type="number"
inputProps={{
maxLength: 20,
}}
style={styles.rootstyle}
erorText="Please enter only 12 digits number"
/>
For people who still are looking for a solution for the number field, this solution worked fine for me.
onInput={(e: any) => {
e.target.value = Math.max(0, parseInt(e.target.value))
.toString()
.slice(0, 2);
}}
Make sure to use any.

Categories

Resources