How to chain on custom validations using Yup - reactjs

I want to add a .test() method to a validationSchema. The test is to look for common email domain misspellings. For instance, to check if gmail was misspelled "gmial" or "gnail".
const validationSchema = Yup.object({
email: Yup.string().lowercase()
.email('You have entered an invalid email')
.required('This field is required')
.test('test-name', 'Did you mean #gmail??????', function(value){
if (value.includes('gmial' || 'gnail'))
return this.createError('')
})
<Formik>
<Form>
<div className='form-control'>
<label htmlFor='email'>Email</label>
<Field type='text' id='email' name='email' />
<ErrorMessage name='email' component={TextError} />
</div>
</Form>
</Formik>

Return a boolean result in your test.
const validationSchema = Yup.object({
email: Yup.string()
.lowercase()
.email('You have entered an invalid email')
.required('This field is required')
.test('Gmail?', 'Did you mean #gmail?', (value) => value.includes('gmial' || 'gnail')),
});

Related

Yup: how to compare one field with other? Ref neither parent.field not working

This is the yup schema:
let schema = yup.object().shape({
password: yup.string().required('Enter password'),
confirm: yup.string().required('Re-enter password')
.test('passwords-match', 'Password and confirmation do not match', function(value){
const t = yup.ref('password'); // some bulky object
const t1 = t.getValue(); // undefined too
const t2 = this.parent.password; // undefined too
const a = this.resolve(t) // undefined even after entering value
return a === value
})
For some reason, comparison with another field is not working.
What am I missing?
Some idea just came to me. Maybe something is wrong with this validator I'm using from here: https://stackblitz.com/edit/react-97lr5s?file=index.js
The full code from there:
import * as yup from 'yup';
let schema = yup.object().shape({
name: yup.string().required(),
age: yup
.number()
.required()
.typeError('Number only.')
.positive()
.integer()
.round(),
a: yup
.number()
.required()
.typeError('Number only.')
.positive()
.integer()
.round().test('', 'asdf', function(v){
console.log(this.parent.name) // undefined
return v == this.parent.age}), // okay?
});
const yupSync = {
async validator({ field }, value) {
await schema.validateSyncAt(field, { [field]: value });
},
};
const DynamicRule = () => {
const [form] = Form.useForm();
return (
<Form form={form} name="form1">
<Form.Item name="name" label="Name" rules={[yupSync]}>
<Input placeholder="Please input your name" />
</Form.Item>
<Form.Item name="age" label="Age" rules={[yupSync]}>
<Input placeholder="Please input age" />
</Form.Item>
<Form.Item name="a" label="Age" rules={[yupSync]}>
<Input placeholder="Please input a" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
Here is how you can compare two fields in Yup. You're almost there but just need some additional info in order to compare two fields.
Working example:
Yup.object().shape({
password: Yup.string().min(8).required("Password is required"),
confirmPassword: Yup.string()
.when("password", { // this should match with input field name
is: (val) => (val && val.length > 0 ? true : false),
then: Yup.string().oneOf(
[Yup.ref("password")],
"Both Password need to be same"
),
})
.required("Confirm Password is required"),
}),

How to get the Yup conditional validation to work with Formik in Reactjs?

I have a Formik form that has two radio buttons that would ask the user to choose if he is a staff member or a student and based on his selection then I will be able to show different fields for the user to fill out using Yup validation.
I am not sure how to use the when() method here !!
Here is my Formik
<Formik
initialValues ={{
name: '',
school: '',
jobType: '',
parentName: '',
parentPhone: '',
parentEmail: '',
staffPhone:'',
staffEmail: '',
condition:'',
}}
onSubmit={async (values) => {
await new Promise((r) => setTimeout(r, 500));
alert(JSON.stringify(values, null, 2));
}}
validationSchema={validate}
>
I am trying to use Yup validation to show the fields based on the users choice
my validation Schema
const validate = Yup.object({
name: Yup.string()
.max(20, "Must be 20 characters or less" )
.required('required'),
school: Yup.string().required('Please select your school').oneOf(schools),
jobType: Yup.string().required("are you a student or a staff member ?!"),
parentName: Yup.string().when('jobType', {
is: true ,
then: Yup.string().required('Field is required')
}),
as for the form itself :
<div id="my-radio-group">Job type</div>
<ErrorMessage name="jobType" />
<div role="group" aria-labelledby="my-radio-group">
<label>
<Field type="radio" name="jobType" value="student" />
Student
</label>
<label>
<Field type="radio" name="jobType" value="staff member" />
Staff member</label>
{console.log({values})}
{/* <div>Picked: {values.jobType}</div> */}
</div>
<button className="btn btn-dark mt-3" type="submit"> Register</button>
You just need to do some conditional rendering of the fields you want based on the value the jobType.
The jobType is expected to be a string that can take on one of the two values "staff member" or "student" depending on the radio selected.
In your case, jobType is being checked against a boolean value true which is wrong. This should be checked against either of the two strings "student" or "staff member".
You should change your validation schema to something like this:
const validate = Yup.object({
name: Yup.string()
.max(20, "Must be 20 characters or less")
.required("Required!"),
school: Yup.string()
.required("Please select your school")
.oneOf(schools),
jobType: Yup.string().required("Are you a student or a staff member?"),
parentName: Yup.string().when('jobType', {
is: "student",
then: Yup.string().required("Field is required")
}),
parentEmail: Yup.string().when('jobType', {
is: "student",
then: Yup.string().required("Field is required")
}),
staffPhone: Yup.string().when('jobType', {
is: "staff member",
then: Yup.string().required("Field is required")
}),
staffEmail: Yup.string().when('jobType', {
is: "staff member",
then: Yup.string().required("Field is required")
})
})
When rendering your form it should be dependent on the values in the jobType like so:
{
values.jobType === "student"
&&
<>
<label>
Parent Name
<Field type="text" name="parentName" placeholder="Enter name" />
</label>
<ErrorMessage name="parentName"/>
<label>
Parent Email
<Field type="text" name="parentEmail" placeholder="Enter name" />
</label>
<ErrorMessage name="parentEmail"/>
</>
}
{
values.jobType === "staff member"
&&
<>
<label>
StaffPhone
<Field type="text" name="staffName" placeholder="Enter name" />
</label>
<ErrorMessage name="staffName"/>
<label>
StaffEmail
<Field type="text" name="staffEmail" placeholder="Enter name" />
</label>
<ErrorMessage name="staffEmail"/>
</>
}
You can also checkout the entire code in my
sandbox here.

Why isn't my Formik form showing warning message? React, Formik

I'm trying to get some basic front end form validation done using Formik. I've validated the user but the error message is not showing on my front end.
I am looking to display an error message iF my form does not meet my validation schema.
My validation schema:
import * as yup from 'yup';
export const userSignUpSchema = yup.object().shape({
name: yup
.string()
.min(4, "Too short")
.max(20, "Too long")
.required("Required"),
email: yup
.string()
.email("Invalid email")
.required("Required"),
password: yup
.string()
.min(4, "Too short")
.max(20, "Too long")
.required("Required"),
confirmPassword: yup
.string()
.min(4, "Too short")
.max(20, "Too long")
.required("Required")
});
My SignUp component
<Formik
initialValues={{
name: '',
email: '',
password: ''
}}
validationSchema={userSignUpSchema}
onSubmit={createUser}
>
<Form className="form">
<label>Display Name</label>
<Field name="name" type="text" onChange={
(e) => {setUsernameReg(e.target.value);
}} />
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
/>
<label>Email Address</label>
<Field name="email" type="email" onChange={
(e) => {setEmailReg(e.target.value);
}} />
<ErrorMessage
name="email"
component="div"
className="invalid-feedback"
/>
<label>Password</label>
<Field name="password" type="password" onChange={
(e) => {setUsernameReg(e.target.value);
}}/>
<ErrorMessage
name="password"
component="div"
className="invalid-feedback"
/>
<button type="submit">Submit</button>
</Form>
</Formik>

Update specific value in Initial Values Formik

I am using Formik and Yup validation for my form which has firstname ,lastname & username. Username should be without spaces so I am using a onChange event and value. Yup validation is working for firstname and Lastname but not for username. When logging my values found out that username is not getting updated. I am a newbie please help me out. Thanks in advance.
const CustomTextInput =({label, ...props}) =>{
const [field, meta] = useField(props);
return(
<>
<label className="required" htmlFor={props.id || props.name}>{label}</label>
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
):null}
<input className="text-input" {...field} {...props}/>
</>
)
}
function App(){
const [regnum, Setreg]= useState("");
function avoid(e){
Setreg(e.target.value.replace(/\s+/g,''));
}
return(
<Styles>
<Formik
initialValues={{
firstname:'',
lastname: '',
username:'',
phone1:'',
email:''
}}
validationSchema={
Yup.object({
firstname: Yup.string()
.required('Required'),
lastname: Yup.string()
.required('Required'),
username: Yup.string()
.min(4,"Username should be greater than 4 characters")
.max(15,"Wooah! Username cannot be that big")
.required('Required'),
})
}
onSubmit= {(values, { setSubmitting , resetForm }) => {
setTimeout(()=> {
//My api call here
resetForm()
setSubmitting(false);
},2000)
}
}>
{props => (
<Form>
<CustomTextInput label="First Name" name="firstname" type="text" placeholder="first Name"/>
<CustomTextInput label="Last Name" name="lastname" type="text" placeholder="Last Name"/>
<CustomTextInput label="UserName" name="username" type="text" value={regnum} onChange={(event)=>avoid(event)} placeholder="Spaces will be removed"/>
<div>
<button type="submit" >{props.isSubmitting ? "Loading..." : "Submit"}</button>
</div>
</Form>
</Formik>
</Styles>
);
}
export default App;
The question is, do you want to prevent the user from using a username with spaces? If so, the easiest way is to do it through yup.
validationSchema={
Yup.object({
firstname: Yup.string()
.required('Required'),
lastname: Yup.string()
.required('Required'),
username: Yup.string()
.required('Required').matches("/\s/", "Cannot contain spaces"),
})
}
Otherwise, if you allow the user to write the username with spaces, but you want it for whichever reasons without spaces, you have to manipulate the data in the submitHandler.
By giving it a custom onChange handler to username, you overrode formik since formik uses it's onChange under the hood. Manipulate the data in the submitHandler, prior to submitting, let formik do it's thing.

Formik submitting a form with empty field

I have below form with Formik and Yup Validation -
<Formik
initialValues={{
email: '',
address: '',
password: ''
}}
validationSchema={ Validation }
onSubmit={values => {
// same shape as initial values
console.log(values);
}}>
{({ errors, status, touched }) => (
<Form>
<FormGroup>
<Label for="exampleEmail">Email</Label>
<Field name="email" type={'email'} component={customInputForm}/>
</FormGroup>
<FormGroup>
<Label for="address">Address</Label>
<Field name="address" type={'text'} component={customInputForm}/>
</FormGroup>
<FormGroup>
<Label for="examplePassword">Password</Label>
<Field name="password" type={'password'} component={customInputForm}/>
</FormGroup>
<Button>Submit</Button>
</Form>
)}
</Formik>
Here when I click on Submit button with empty field form is submitting without any error but when I touch the input then it is giving error. Not sure what I am doing wrong here I want to check validation on Submit Button Click also.
Below is Yup Error Message -
const Validation = Yup.object().shape({
address: Yup.string()
.min(2, "Too Short!")
.max(50, "Too Long!")
.required("Required"),
password: Yup.string()
.min(2, "Too Short!")
.max(50, "Too Long!")
.required("Required"),
email: Yup.string()
.email("Invalid email")
.required("Required")
});
export default Validation;

Resources