Formik with patch api call in Reactjs - javascript

i haven't work with rest api and formik so need guidance regarding that.
i have EDIT form where i have one user against id , i want to set user details on form and submit it with updated values and without any error if it kept unchanged.
for this i have to set values of Text Fields somehow and call patch api on submit
this is my form code :
<form onSubmit={handleSubmit}
>
<label className="userlabels">Username</label>
<TextField
name="userName"
placeholder="User Name"
type="text"
id="userName"
onBlur={handleBlur}
value={values.userName}
onChange={handleChange}
error={touched.userName && Boolean(errors.userName)}
helperText={touched.userName && errors.userName}
/>
<label className="userlabels">Fullname</label>
<TextField
name="fullName"
placeholder="Full Name"
type="text"
id="fullName"
onBlur={handleBlur}
value={values.fullName}
onChange={handleChange}
error={touched.fullName && Boolean(errors.fullName)}
helperText={touched.fullName && errors.fullName}
/>
<label className="userlabels">Email</label>
<TextField
name="email"
placeholder="Email"
type="email"
id="email"
onBlur={handleBlur}
value={values.email}
onChange={handleChange}
error={touched.email && Boolean(errors.email)}
helperText={touched.email && errors.email}
/>
<label className="userlabels">Password</label>
<TextField
sx={{
width: "55%",
marginBottom: "4vh",
}}
name="password"
placeholder="password"
type={passwordShown ? "text" : "password"}
id="password"
autoComplete="off"
onBlur={handleBlur}
value={values.password}
onChange={handleChange}
error={touched.password && Boolean(errors.password)}
helperText={touched.password && errors.password}
/>
<FormControl>
<label className="userlabels" id="role-label">
Role
</label>
<Select
name="role"
labelId="role-label"
id="role"
onChange={handleChange}
onBlur={handleBlur}
value={values.role}
error={touched.role && Boolean(errors.role)}
>
{role.map((option, index) => (
<MenuItem key={index} value={option.role}>
{option.role}
</MenuItem>
))}
</Select>
<FormHelperText error>
{touched.role && errors.role}
</FormHelperText>
</FormControl>
<label className="userlabels">Organization</label>
<TextField
sx={{
width: "55%",
marginBottom: "4vh",
}}
name="organ"
placeholder="Organization"
type="text"
id="organ"
onBlur={handleBlur}
value={[values.organ]}
onChange={handleChange}
error={touched.organ && Boolean(errors.organ)}
helperText={touched.organ && errors.organ}
/>
<FormControl>
<label className="userlabels" id="active-label">
Active
</label>
<Select
name="active"
labelId="active-label"
id="active"
onChange={handleChange}
onBlur={handleBlur}
value={values.active}
error={touched.active && Boolean(errors.active)}
>
{active.map((option, index) => (
<MenuItem key={index} value={option.id}>
{option.active}
</MenuItem>
))}
</Select>
<FormHelperText error>
{touched.active && errors.active}
</FormHelperText>
</FormControl>
<Button
variant="contained"
type="submit">
Update
</Button>
</form>
and this is formik code
const initialValues = {
fullName: "",
email: "",
userName: "",
organ: [] ,
password: "",
role: [],
active: "",
};
const { values, errors, touched, handleBlur, handleChange, handleSubmit } =
useFormik({
initialValues,
validationSchema: UserSchema,
onSubmit: (values, action) => {
fetch(`${state.baseUrl}/users/${id}`, {
method: "PATCH",
body: JSON.stringify({
fullname: values.fullName,
email: values.email,
username: values.userName,
organization: [values.organ],
password: values.password,
role: [values.role],
is_active: values.active,
}),
headers: {
"Content-type": "application/json",
},
})
.then((response) => {
if (response.status === 200) {
setAlert(true);
setMsg("You Are Successfully Updated the User");
setType("success");
return Promise.resolve();
} else {
setAlert(true);
setMsg("Some Thing Went Wrong , Please Try Again Later.");
setType("error");
return Promise.reject("failed");
}
})
.catch((error) => {
setAlert(true);
setMsg("Some Thing Went Wrong , Please Try Again Later.");
setType("error");
console.error("There was an error!", error);
});
action.resetForm();
},
});
i want to get pre-filled form and updated on submission

Related

How to use post country code in input using react-phone-input-2

I am building a simple form that contains a few inputs with form validation. I am using here Formik and yup for form validation. I am using here phone input with their postal country code react-phone-input-2. All things are going well. But I want some help with the Great phone input I am using the default country us but It is not giving me the output in my string when I click on submit also how to use all the country codes in react-phone-input not only us or any country, I want all country postal code in my output when I click on submit button.
import "./FormInput.css";
import { Formik } from "formik";
import * as Yup from "yup";
import PhoneInput from "react-phone-input-2";
import "react-phone-input-2/lib/style.css";
const validate = Yup.object({
username: Yup.string()
.min(2, "Must be 2 chracters or more")
.max(15, "Must be 15 chracters or less")
.required("Username field is required!"),
email: Yup.string()
.email("Email is Invalid")
.required("Email field is required!"),
phone: Yup.number().max(15, "Phone number must be 15 or less"),
company: Yup.string().required("Company field is required!"),
website: Yup.string()
.matches(
/((https?):\/\/)?(www.)?[a-z0-9]+(\.[a-z]{2,}){1,3}(#?\/?[a-zA-Z0-9#]+)*\/?(\?[a-zA-Z0-9-_]+=[a-zA-Z0-9-%]+&?)?$/,
"Enter correct url!"
)
.required("Website field is required!"),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(
/^[a-zA-Z0-9!##$%^&*]{6,16}$/,
"password should contain atleast one number and one special character"
)
.required("Passowrd field is required!"),
confirmPassword: Yup.string()
.oneOf([Yup.ref("password"), null])
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(
/^[a-zA-Z0-9!##$%^&*]{6,16}$/,
"password should contain atleast one number and one special character"
)
.required("Passowrd field is required!")
});
const FormInput = () => {
return (
<>
<Formik
initialValues={{
username: "",
email: "",
phone: "",
company: "",
website: "",
password: "",
confirmPassword: ""
}}
validationSchema={validate}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting
/* and other goodies */
}) => (
<form className="form" onSubmit={handleSubmit}>
<h2>Fill the Form</h2>
<div className="form-control">
<input
id="username"
type="username"
name="username"
onChange={handleChange}
onBlur={handleBlur}
value={values.username}
placeholder="Enter username"
/>
<p>{errors.username && touched.username && errors.username}</p>
</div>
<div className="form-control">
<input
id="email"
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
placeholder="Enter email"
/>
<p>{errors.email && touched.email && errors.email}</p>
</div>
<div className="form-control">
<PhoneInput
id="phone"
placeholder="Enter phone number"
onChange={handleChange}
onBlur={handleBlur}
value={values.phone}
type="number"
inputProps={{
name: "phone",
required: true,
autoFocus: true
}}
country={`us`}
inputClass="phone-input"
inputStyle={{
width: "100%",
height: "42px"
}}
/>
<p>{errors.phone && touched.phone && errors.phone}</p>
</div>
<div className="form-control">
<input
id="company"
type="text"
name="company"
onChange={handleChange}
onBlur={handleBlur}
value={values.company}
placeholder="Enter company name"
/>
<p>{errors.company && touched.company && errors.company}</p>
</div>
<div className="form-control">
<input
id="website"
type="url"
name="website"
onChange={handleChange}
onBlur={handleBlur}
value={values.website}
placeholder="Enter website url"
/>
<p>{errors.website && touched.website && errors.website}</p>
</div>
<div className="form-control">
<input
id="password"
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
placeholder="Enter password"
/>
<p>{errors.password && touched.password && errors.password}</p>
</div>
<div className="form-control">
<input
id="confirmPassword"
type="password"
name="confirmPassword"
onChange={handleChange}
onBlur={handleBlur}
value={values.confirmPassword}
placeholder="Enter confirm password"
/>
<p>
{errors.confirmPassword &&
touched.confirmPassword &&
errors.confirmPassword}
</p>
</div>
<button type="submit" className="btn" disabled={isSubmitting}>
Submit
</button>
</form>
)}
</Formik>
</>
);
};
export default FormInput;

Why my handleChange is not being invoked? MaterialUI

Is there something wrong with the TextField component from Material UI that I'm missing?
My handleChange simply is not getting invoked.
I can type in value in the inputs, but state of the component is not changing.
Here is the Auth component:
const Auth = () => {
const [formData, setFormData] = useState(initialFormState)
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
}
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
})
}
return (
<Container component="main" maxWidth="xs">
<Paper className={classes.paper} elevation={3}>
<form className={classes.form} onSubmit={handleSubmit}>
<Grid container spacing={2}>
<Input name="email" label="e-mail" handleChange={handleChange} type="email" />
<Input name="password" label="password" handleChange={handleChange} type={showPassword ? "text" : "password"} handleShowPassword={handleShowPassword} />
</Grid>
<Button type="submit" variant="contained" color="primary" className={classes.submit} fullWidth>
{isSignup ? "Sign up" : "Sign in"}
</Button>
</form>
</Paper>
</Container>
);
}
And the input component which is inside Auth:
const Input = ({ name, half, handleChange, type, label, autoFocus, handleShowPassword }) => {
return (
<>
<Grid item xs={12} sm={half ? 6 : 12}>
<TextField
name={name}
handleChange={handleChange}
variant="outlined"
required
fullWidth
label={label}
autoFocus={autoFocus}
type={type}
InputProps={name === "password" ? {
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={handleShowPassword}>
{type === "password" ? <Visibility/> : <VisibilityOff/>}
</IconButton>
</InputAdornment>
)
} : null}
/>
</Grid>
</>
);
}
You've to change the input attribute from handleChange to onChange
<Input name="email" label="e-mail" onChange={handleChange} type="email" />
<Input name="password" label="password" onChange={handleChange} type={showPassword ? "text" : "password"} handleShowPassword={handleShowPassword} />

React. Form validation

I have a multistep form for signing up. What is the easiest and the best approach for form validation? What should I use? Is it alright to use Formik for that? Could you suggest to me what to do?
Here is one of the forms:
return(
<Container>
<Row className="justify-content-center">
<Col md="8">
<Card border="dark">
<Card.Title className="text-center">New User</Card.Title>
<Card.Body>
<Form>
<Form.Group controlId="formGridFirstName">
<Form.Label>First Name</Form.Label>
<Form.Control type="text" value={formValues.firstName} onChange={handleChange('firstName')} />
</Form.Group>
<Form.Group controlID="formGridLastName">
<Form.Label>Last Name</Form.Label>
<Form.Control type="text" value={formValues.lastName} onChange={handleChange('lastName')} />
</Form.Group>
<Form.Group controlID="formGridEmail">
<Form.Label>Email</Form.Label>
<Form.Control type="email" value={formValues.email} onChange={handleChange('email')} />
</Form.Group>
<Form.Group controlId="formGridPassword">
<Form.Label>Password</Form.Label>
<Form.Control type="password" value={formValues.password} onChange={handleChange('password')} />
</Form.Group>
<Form.Group controlId="formGridPhone">
<Form.Label>Phone</Form.Label>
<Form.Control type="tel" value={formValues.phone} onChange={handleChange('phone')} />
</Form.Group>
<Button variant="light" type="submit" size="lg" onClick={redirectToHome}>Cancel</Button>
<Button variant="primary" type="submit" size="lg" onClick={saveAndContinue}>Next</Button>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
};
You can use Formik and a Yup for validation schema:
https://formik.org/docs/api/formik#validationschema-schema----schema
npm links:
https://www.npmjs.com/package/formik
https://www.npmjs.com/package/yup
It will look like this:
export default function MyForm() {
const initValues = {
firstName: "",
lastName: "",
email: "",
password: "",
phone: ""
};
const schema = Yup.object().shape({
firstName: Yup.string(),
lastName: Yup.string(),
email: Yup.string().email("Email format is invalid."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/[a-zA-Z]/, "Password can only contain Latin letters."),
phone: Yup.string().phone(" ")
});
return (
<Formik validationSchema={schema} initialValues={initValues}>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
setFieldTouched
} = props;
return (
<Container>
<Row className="justify-content-center">
<Col md="8">
<Card border="dark">
<Card.Title className="text-center">New User</Card.Title>
<Card.Body>
<Form>
<Form.Group controlId="formGridFirstName">
<Form.Label>First Name</Form.Label>
<Form.Control
name="firstName"
type="text"
value={values.firstName}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlID="formGridLastName">
<Form.Label>Last Name</Form.Label>
<Form.Control
name="lastName"
type="text"
value={values.lastName}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlID="formGridEmail">
<Form.Label>Email</Form.Label>
<Form.Control
name="email"
type="email"
value={values.email}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlId="formGridPassword">
<Form.Label>Password</Form.Label>
<Form.Control
name="password"
type="password"
value={values.password}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlId="formGridPhone">
<Form.Label>Phone</Form.Label>
<Form.Control
name="phone"
type="tel"
value={values.phone}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Button
variant="light"
type="submit"
size="lg"
onClick={redirectToHome}
>
Cancel
</Button>
<Button
variant="primary"
type="submit"
size="lg"
onClick={saveAndContinue}
>
Next
</Button>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
}}
</Formik>
);
}
But you should change also your Form.Control component:
Add
import { ErrorMessage, getIn } from "formik";
const error = getIn(props.errors, props.name);
and add to your Input these attributes
onChange={(e) => {setFieldTouched("firstName");handleChange(e);}
error={error}
and Error to show
<Error component="div" name={props.name} />
I would suggest adding your own validators and bundling inputs that are of the same type. For example First Name and Last Name are both strings so you can have a validator for string types. You could do it like this
//validator.js
const stringValidator = (value) => {
if(isEmpty(value)){
return { isValid: false, errors: ["Field cannot be blank"] }
}
return { isValid: true, errors: [] }
}
const config = {
firstName: stringValidator,
lastName: stringValidator,
email: emailValidator,
cellphone: cellphoneValidator,
password: passwordValidator
}
const getValidator = (questionId) => config[questionId];
export default (questionId, value) => {
const validate = getValidator(questionId);
return validate(value)
}
...
// Form.js class
answerQuestion(questionId){
return (e) => {
const answer = e.target.value;
const validation = validate(questionId, answer)
const { errors } = validation;
let { fields } = this.state;
fields[questionId] = { errors, value, label: field.label }
this.setState({ fields })
}
}
...
<Form>
{this.state.fields.map( field => {
return(
<Form.Group>
<Form.Label>{field.label}</Form.Label>
<Form.Control type="text" value={field.value} onChange={this.answerQuestion(field.questionId)} />
{field.errors.map(err => <span>{err}</span>}
</Form.Group>
)
}
</Form>

How to append inputs with formik in react

So this may be something simple but I'm hitting a roadblock. I want to take in a form input but can't seem to figure out how to append the value correctly so its appending string is captured.
I'm using Formik with yup in react.
<InputGroup>
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
className={
"form-control" +
(errors.domain_url && touched.domain_url
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="domain_url"
component="div"
className="invalid-feedback"
/>
<InputGroupAddon addonType="append">
.localhost
</InputGroupAddon>
</InputGroup>
Any help would be appreciated. I just want to get the .localhost to be automatically added to the input items. for this field. I thought I could do something like value=({values.domain_url} + ".localhost") but that didn't seem to work and as you may already tell I am very new to javascript.
Thank you!
Full code below, I'm also having issues with datepicker displaying within the formik state, and then there's how to even get the values to push to my getTenant(function) to be passed to my api.
static propTypes = {
addTenant: PropTypes.func.isRequired,
};
onSubmit = (values) => {
values.preventDefault();
this.props.addTenant(values);
};
render() {
const {
domain_url,
schema_name,
name,
config,
} = this.state;
const TenantSchema = Yup.object().shape({
domain_url: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client URL header is required"),
schema_name: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client db name is required"),
name: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client name is required"),
});
return (
<div className={s.root}>
<Formik
initialValues={{
domain_url: "",
schema_name: "",
client_name: "",
config: [
{
date: "",
Tenant_description: "",
},
],
}}
// validationSchema={TenantSchema} this is commented off because it breaks
submittions
onSubmit={(values, { setSubmitting, resetForm }) => {
setSubmitting(true);
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
resetForm();
setSubmitting(false);
}, 100);
}}
//onSubmit={onSubmit}
>
{({
values,
errors,
status,
touched,
handleBlur,
handleChange,
isSubmitting,
setFieldValue,
handleSubmit,
props,
}) => (
<FormGroup>
<Form onSubmit={handleSubmit}>
<legend>
<strong>Create</strong> Tenant
</legend>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Show URL
</Label>
<Col md={7}>
<InputGroup>
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
onSubmit={(values) => {
values.domain_url = values.domain_url + ".localhost";
}} //this isn't working
className={
"form-control" +
(errors.domain_url && touched.domain_url
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="domain_url"
component="div"
className="invalid-feedback"
/>
<InputGroupAddon addonType="append">
.localhost
</InputGroupAddon>
</InputGroup>
</Col>
</FormGroup>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Database Name
</Label>
<Col md={7}>
<Field
name="schema_name"
type="text"
className={
"form-control" +
(errors.schema_name && touched.schema_name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="schema_name"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Name
</Label>
<Col md={7}>
<Field
name="name"
type="text"
className={
"form-control" +
(errors.name && touched.name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FieldArray
name="config"
render={(arrayHelpers) => (
<div>
{values.config.map((config, index) => (
<div key={index}>
<FormGroup row>
<Label
md={4}
className="text-md-right"
for="mask-date"
>
Tenant Description
</Label>
<Col md={7}>
<TextareaAutosize
rows={3}
name={`config.${index}.tenant_description`}
id="elastic-textarea"
type="text"
onReset={values.event_description}
placeholder="Quick description of tenant"
onChange={handleChange}
value={values.tenant_description}
onBlur={handleBlur}
className={
`form-control ${s.autogrow} transition-height` +
(errors.tenant_description &&
touched.tenant_description
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="tenant_description"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FormGroup row>
<Label
for="normal-field"
md={4}
className="text-md-right"
>
Date
</Label>
<Col md={7}>
<DatePicker
tag={Field}
name={`config.${index}.date`}
type="date"
selected={values.date}
value={values.date}
className={
"form-control" +
(errors.date&& touched.date
? " is-invalid"
: "")
}
onChange={(e) =>
setFieldValue("date", e)
}
/>
<ErrorMessage
name="date"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
</div>
))}
</div>
)}
/>
<div className="form-group">
<button
type="submit"
disabled={isSubmitting}
className="btn btn-primary mr-2"
>
Save Tenant
</button>
<button type="reset" className="btn btn-secondary">
Reset
</button>
</div>
</Form>
<Col md={7}>{JSON.stringify(values)}</Col>
</FormGroup>
)}
</Formik>
</div>
);
}
}
export default connect(null, { addTenant })(TenantForm);
You could use setFieldValue if you want to customize the value
https://jaredpalmer.com/formik/docs/api/formik#setfieldvalue-field-string-value-any-shouldvalidate-boolean--void
You can add onChange
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
onChange={st => {
let value = st.target.value;
let suffix = ".localhost";
let index = value.indexOf(".localhost");
if (index > 0) {
suffix = "";
}
//add suffix 'localhost' if it is not already added
props.setFieldValue("domain_url", value + suffix);
}}
className={
"form-control" +
(errors.domain_url && touched.domain_url ? " is-invalid" : "")
}
/>;
But adding suffix is more preferable on onSubmit:
onSubmit = {(values, actions) => {
console.log('valuesbefore',values)
values.domain_url= values.domain_url+ ".localhost"
console.log('valuesafter',values)
this.props.addTenant(values);
};

Async/Await with Formik?

I am using formik and I am wondering how to use async/await with onSubmit
<Formik
initialValues={{ email: '' }}
onSubmit={(values, { setSubmitting }) => {
// how to do async/await here.
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.email()
.required('Required'),
})}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset,
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email" style={{ display: 'block' }}>
Email
</label>
<input
id="email"
placeholder="Enter your email"
type="text"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.email && touched.email ? 'text-input error' : 'text-input'
}
/>
{errors.email &&
touched.email && <div className="input-feedback">{errors.email}</div>}
<button
type="button"
className="outline"
onClick={handleReset}
disabled={!dirty || isSubmitting}
>
Reset
</button>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</form>
);
}}
</Formik>
Is something like this what you're looking for?
onSubmit={ async (values, { setSubmitting }) => {
await ...
setSubmitting(false)
}}
As alternative, you could make an seperate function as well.
async function _handleSubmit(values) {
await...
}
onSubmit={(values, { setSubmitting }) => {
_handleSubmit(values)
setSubmitting(false)
}}

Categories