Yup validation for multiple checkboxes - javascript

I need to create the below field with multiple checkboxes. I need the following validations:
At least one of them should be selected
If its the other field user need to add the text to the input field
I need to write a yup validation to achieve this.

At least one of them should be selected:
This is how you'll do that logic using yup validation rules
const validationSchema = yup.object().shape({
assets: yup.array().min(1).of(yup.string().required()).required(),
});
If its the other field user need to add the text to the input field:
You can also do this logic using Formik but I suggest you to create a local state for "assets" and if you wanna create new asset using text field just push that new asset in assets local field and then it will automatically render that new asset on the screen.
And then you can checked or unchecked that new asset. I hope you got the point.
This is the code snippet you can check!
import { useState } from "react";
import * as yup from "yup";
import { FieldArray, Formik } from "formik";
const initialValues = {
assets: [],
};
const validationSchema = yup.object().shape({
assets: yup.array().min(1).of(yup.string().required()).required(),
});
function Form() {
const [newAsset, setNewAsset] = useState("");
const [assets, setAssets] = useState([
"Property",
"Motor Vehicles",
"Financial Assets",
]);
const handleAddNewAsset = () => {
if (newAsset) {
setAssets([...assets, newAsset]);
setNewAsset("");
}
};
return (
<>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values) => {
console.log("form values if validation succeed: ", values);
}}
>
{(props) => (
<form onSubmit={props.handleSubmit}>
<FieldArray
name="assets"
render={(arrayHelpers) => (
<>
{assets.map((asset, idx) => (
<label className="form-check-label my-1">
<input
name={`assets.${idx}`}
type="checkbox"
className="form-check-input"
value={asset}
checked={props.values.assets.includes(asset)}
onChange={(e) => {
if (e.target.checked) {
arrayHelpers.push(asset);
} else {
const index = props.values.assets.indexOf(asset);
arrayHelpers.remove(index);
}
}}
/>
<span className="mx-2">{asset}</span>
</label>
))}
</>
)}
/>
<input
type="text"
value={newAsset}
onChange={(e) => setNewAsset(e.target.value)}
/>
<button type="button" onClick={handleAddNewAsset}>
Others (specify)
</button>
</form>
)}
</Formik>
</>
);
}
I hope it will help.
Thanks!

For Question 1 - At least one of them should be selected
Try below validationSchema
const validationSchema = Yup.object({checked: Yup.array().min(1, 'Select atleast one option of your interest')
});
in FormIk use initialValues like below:
<Formik initialValues={{checked: []}} />
For Question 2 - Other field validation you can check yup api documents.

Related

Handle Reset for specific field formik

I am new to react and I have just started using Formik
I like how simple it makes making forms and handling forms in react.
I have created multiple custom fields using formik, I am putting the react-select field I created as an example here.
import { ErrorMessage, Field } from "formik";
import React from "react";
import Select from 'react-select'
const SelectInput = (props) => {
const { label, name, id,options, required, ...rest } = props;
const defaultOptions = [
{label : `Select ${label}`,value : ''}
]
const selectedOptions = options ? [...defaultOptions,...options] : defaultOptions
return (
<div className="mt-3">
<label htmlFor={id ? id : name}>
{label} {required && <span className="text-rose-500">*</span>}
</label>
<Field
// className="w-full"
name={name}
id={id ? id : name}
>
{(props) => {
return (
<Select
options={selectedOptions}
onChange={(val) => {
props.form.setFieldValue(name, val ? val.value : null);
}}
onClick = {(e)=>{e.stopPropagation}}
{...rest}
// I want someting like onReset here
></Select>
);
}}
</Field>
<ErrorMessage
name={name}
component="div"
className="text-xs mt-1 text-rose-500"
/>
</div>
);
};
export default SelectInput;
This is the usual code I use for submitting form as you can see I am using resetForm() method that is provided by formik, I want to attach the reseting logic in on submit method itself.
const onSubmit = async (values, onSubmitProps) => {
try {
//send request to api
onSubmitProps.resetForm()
} catch (error) {
console.log(error.response.data);
}
};
If you want to reset the selected value after the form is submitted, you need to provide a controlled value for the Select component.
The Formik Field component provides the value in the props object, so you can use it.
For example:
SelectInput.js
import { ErrorMessage, Field } from 'formik';
import React from 'react';
import Select from 'react-select';
const SelectInput = ({ label, name, id, options, required, ...rest }) => {
const defaultOptions = [{ label: `Select ${label}`, value: '' }];
const selectedOptions = options ? [...defaultOptions, ...options] : defaultOptions;
return (
<div className='mt-3'>
<label htmlFor={id ? id : name}>
{label} {required && <span className='text-rose-500'>*</span>}
</label>
<Field
// className="w-full"
name={name}
id={id ? id : name}
>
{({
field: { value },
form: { setFieldValue },
}) => {
return (
<Select
{...rest}
options={selectedOptions}
onChange={(val) => setFieldValue(name, val ? val : null)}
onClick={(e) => e.stopPropagation()}
value={value}
/>
);
}}
</Field>
<ErrorMessage name={name} component='div' className='text-xs mt-1 text-rose-500' />
</div>
);
};
export default SelectInput;
and Form.js
import { Formik, Form } from 'formik';
import SelectInput from './SelectInput';
function App() {
return (
<Formik
initialValues={{
firstName: '',
}}
onSubmit={async (values, { resetForm }) => {
console.log({ values });
resetForm();
}}
>
<Form>
<SelectInput
name='firstName'
label='First Name'
options={[{ label: 'Sam', value: 'Sam' }]}
/>
<button type='submit'>Submit</button>
</Form>
</Formik>
);
}
export default App;
Therefore, if you click the Submit button, value in the Select component will be reset.
You can also make a useRef hook to the Fromik component and then reset the form within the reset function without adding it as a parameter to the function.
https://www.w3schools.com/react/react_useref.asp
It's one of the really nice hooks you'll learn as you progress through React :)
So if I understood you correctly you want to reset a specif field value onSubmit rather than resetting the whole form, that's exactly what you can achieve using actions.resetForm().
Note: If nextState is specified, Formik will set nextState.values as the new "initial state" and use the related values of nextState to update the form's initialValues as well as initialTouched, initialStatus, initialErrors. This is useful for altering the initial state (i.e. "base") of the form after changes have been made.
You can check this in more detail here.
And here is an example of resetting a specific field using resetForm() whereby you can see as you input name, email and upon submit only email field will get empty using resetForm.
import "./styles.css";
import React from "react";
import { Formik } from "formik";
const initialState = {
name: "",
email: ""
};
const App = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={initialState}
onSubmit={(values, actions) => {
console.log(values, "values");
actions.resetForm({
values: {
email: initialState.email
}
});
}}
>
{(props) => (
<form onSubmit={props.handleSubmit}>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
<br />
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.email}
name="email"
/>
<br />
<br />
{props.errors.name && <div id="feedback">{props.errors.name}</div>}
<button type="submit">Submit</button>
</form>
)}
</Formik>
</div>
);
export default App;

Use select in Semantic UI with Formik

I'm usign Formik to validate some data fields with Semantic UI in React. It works fine with input fields but doesn't work with selectors.
How it works with input fields:
import { Formik, Form, Field } from 'formik';
import { Input, Button, Select, Label, Grid } from 'semantic-ui-react';
import * as Yup from 'yup';
...
const initialValues = {
name: ''
};
const requiredErrorMessage = 'This field is required';
const validationSchema = Yup.object({ name: Yup.string().required(requiredErrorMessage) });
<Formik
htmlFor="amazing"
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={values => this.handleSubmit(values)}>
{({ errors, touched }) => (
<Form id="amazing">
<div>
<CreationContentContainer>
<Grid>
<Grid.Column>
<Label>Company name</Label>
<Field name="name" as={Input} placeholder="name" /> // here is the input
<div>{touched.name && errors.name ? errors.name : null}</div>
</Grid.Column>
</Grid>
</CreationContentContainer>
<Button type="submit" form="amazing">
Create company
</Button>
</div>
</Form>
)}
</Formik>;
However, when the as={Input} is replaced by as={Select}, it doesn't work. The dropdown gets opened when I click on the selector, it shows the options (company1 and company2), I click on one of them but it does not work -> the value shown is still the placeholder value.
import { Formik, Form, Field } from 'formik';
import { Input, Button, Select, Label, Grid } from 'semantic-ui-react';
import * as Yup from 'yup';
const companyOptions = [ // the select's options
{ text: 'company1', value: 'company1' },
{ text: 'company2', value: 'company2' },
];
<Formik
htmlFor="amazing"
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={values => this.handleSubmit(values)}>
{({ errors, touched }) => (
<Form id="amazing">
<div>
<CreationContentContainer>
<Grid>
<Grid.Column>
<Label>Company name</Label>
<Field
name="name"
as={Select} // here is changed to Select
options={companyOptions} // the options
placeholder="name"
/>
<div>{touched.name && errors.name ? errors.name : null}</div>
</Grid.Column>
</Grid>
</CreationContentContainer>
<Button type="submit" form="amazing">
Create company
</Button>
</div>
</Form>
)}
</Formik>;
The reason is because Formik passes an onChange function into the component. However, the Select (which is just syntactic sugar for dropdown https://react.semantic-ui.com/addons/select/) onChange prop uses a function of shape handleChange = (e: React.SyntheticEvent<HTMLElement>, data: SelectProps) => this.setState({value: data.value}) (https://react.semantic-ui.com/modules/dropdown/#usage-controlled) which is different than the conventional handleChange = (event: React.SyntheticEvent<HTMLElement>) => this.setState({value: event.target.value}) which formik uses.
So an easy solution would be to use the Formik function setFieldValue.
In your code, the implementation would be something like this:
const companyOptions = [ // the select's options
{ text: 'company1', value: 'company1' },
{ text: 'company2', value: 'company2' },
];
<Formik
htmlFor="amazing"
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={values => this.handleSubmit(values)}>
{({ errors, touched, setFieldValue, values }) => {
const handleChange = (e, { name, value }) => setFieldValue(name, value));
return (
<Form id="amazing">
<div>
<CreationContentContainer>
<Grid>
<Grid.Column>
<Label>Company name</Label>
<Field
name="name"
as={Select} // here is changed to Select
options={companyOptions} // the options
placeholder="name"
onChange={handleChange}
value={values.name}
/>
<div>{touched.name && errors.name ? errors.name : null}</div>
</Grid.Column>
</Grid>
</CreationContentContainer>
<Button type="submit" form="amazing">
Create company
</Button>
</div>
</Form>
)}
}
</Formik>;
Here's a somewhat working version using your code. The fields update (verified with a console.log), but the submit button isn't working 🤷‍♂️: https://codesandbox.io/s/formik-and-semanticui-select-gb7o7
This is a somewhat incomplete solution and does not handle the onBlur and other injected formik functions. It also defeats the purpose of Formik somewhat. A better solution would be to create a HOC that wraps semantic ui components to use Fromik correctly: https://github.com/formium/formik/issues/148.
And lastly, you can skip the Semantic UI Select and use the Semantic UI HTML controlled select: https://react.semantic-ui.com/collections/form/#shorthand-field-control-html. Notice the select vs Select. This will work natively with Formik without the need to pass in onChange and value.
I recently created a binding library for Semantic UI React & Formik.
Link: https://github.com/JT501/formik-semantic-ui-react
You just need to import Select from the library and fill in name & options props.
// Import components
import { Select, SubmitButton } from "formik-semantic-ui-react";
const companyOptions = [ // the select's options
{ text: 'company1', value: 'company1' },
{ text: 'company2', value: 'company2' },
];
<Formik
htmlFor="amazing"
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={values => this.handleSubmit(values)}>
{({ errors, touched }) => (
<Form id="amazing">
<div>
<CreationContentContainer>
<Grid>
<Grid.Column>
<Label>Company name</Label>
<Select
name="name"
selectOnBlur={false}
clearable
placeholder="name"
options={companyOptions}
/>
</Grid.Column>
</Grid>
</CreationContentContainer>
<SubmitButton>
Create company
</SubmitButton>
</div>
</Form>
)}
</Formik>;

Trying to make a Formik multistep with validation. How can I validate fields?

I am trying to make a field validation in a formik. The problem is that I made a multi-step form, my approach is that I made an array of components and I use them based on pages[state] and I don't know how to send props to that.
I want to validate the field to take only two digit numbers, and to show a error message if it doesn't
const SignUp = () => {
const state = useSelector((state: RootState) => state);
console.log("state", state);
const dispatch = useDispatch();
return (
<Formik
initialValues={{ firstName: "", lastName: "", email: "" }}
onSubmit={values => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 500);
}}
render={({ errors, touched, isValidating }) => (
<Form>
<div>{pages[state]}</div>
<button
type="button"
onClick={() => {
dispatch(decrement());
}}
>
Prev
</button>
<button
type="button"
onClick={() => {
pages.length - 1 <= state
? console.log("No more pages")
: dispatch(increment());
}}
>
Next
</button>
</Form>
)}
/>
);
};
const Page2 = () => {
return (
<>
<h1>
What is your <span id="idealWeightText">ideal weight</span> that you
want to reach?
</h1>
<Input
name={"firstName"}
htmlFor={"firstName"}
type={"number"}
validation={validateKilograms}
/>
</>
);
};
const Input: React.FC<FieldProps> = ({
name,
onChange,
htmlFor,
type,
validation,
placeholder
}) => {
return (
<>
<label htmlFor={name}>
<Field
type={type}
name={name}
placeholder={placeholder}
validate={validation}
onChange={onChange}
/>
kg
</label>
</>
);
};
There is a library called Yup, That You can use, You will get freedom to set conditional validations.
Yup is best suited with formik for validations.
After Every step You can enable a flag and set the validation accordingly.
Also I would suggest you to use withFormik. Instead of formik because there you can make a separate file for validations and can give its path. So that will also improve your folder structure.

How to receive select value in handlesubmit function with formik react js?

i am developing a form in reactjs using formik plugin plugin link. when i submit form i am getting text fields values but dropdown values are coming empty...
this is my dropdown select
<div className="form-group">
<Field component="select" id="category" name="category" value={this.state.value} className={"form-control"} onChange={ this.handleChange }>
<option value="lokaler">Lokaler</option>
<option value="jobb">Jobb</option>
<option value="saker-ting">Saker & ting</option>
<option value="evenemang">Evenemang</option>
</Field>
</div>
handle submit function
export default withFormik({
enableReinitialize: true,
mapPropsToValues({ category }) {
return {
category: category || ''
}
},
handleSubmit(values, { setStatus, setErrors }){
console.log("data is this: ");
console.log(values); //here i am getting all form fields values except select value returning empty value
console.log("category: "+values.category);// here i always get empty value but not getting selected value
}
i am getting all text fields values in handle submit function but i am not able to get dropdown selected value....kindly tell me how to get dropdown select value in handle submit function ?
I also faced this problem yesterday. My problem was to submit form that contains ant design dropdown. I finally make it work after hours of:
revisiting the Formik Docs
watch Andrew Mead's video Better React Form with Formik again.
also viewing Jared Palmer's Working with 3rd-party inputs #1: react-select
The doc said you need to set onChange, onBlur events to setFieldValue, setFieldTouched props respectively like 3rd-party input example:
<MySelect
value={values.topics}
onChange={setFieldValue}
onBlur={setFieldTouched}
error={errors.topics}
touched={touched.topics}
/>
So to my problem, I need to change a bit:
<Select
{...field}
onChange={(value) => setFieldValue('fruitName', value)}
onBlur={()=> setFieldTouched('fruitName', true)}
value={values.fruitName}
...
>
...
</Select>
Hope this will help.
Here is my CodeSandbox
A more robust way to handle select components whilst using Formik would be to also use Jed Watsons react-select component. The two work together nicely and abstract away a lot of the boilerplate you would normally need to implement to get the component working seamlessly with Formik.
I have forked a simple example from Jared Palmer's react-select / Formik example on codesandbox and adjusted it to reflect your code above.
import "./formik-demo.css";
import React from "react";
import { render } from "react-dom";
import { withFormik } from "formik";
import Select from "react-select";
import "react-select/dist/react-select.css";
const options = [
{ value: "lokaler", label: "Lokaler" },
{ value: "jobb", label: "Jobb" },
{ value: "saker-ting", label: "Saker & ting" },
{ value: "evenemang", label: "Evenemang" }
];
const MyForm = props => {
const {
values,
handleChange,
handleBlur,
handleSubmit,
setFieldValue
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="myText" style={{ display: "block" }}>
My Text Field
</label>
<input
id="myText"
placeholder="Enter some text"
value={values.myText}
onChange={handleChange}
onBlur={handleBlur}
/>
<MySelect value={values.option} onChange={setFieldValue} />
<button type="submit">Submit</button>
</form>
);
};
class MySelect extends React.Component {
handleChange = value => {
// this is going to call setFieldValue and manually update values.topcis
this.props.onChange("option", value);
};
render() {
return (
<div style={{ margin: "1rem 0" }}>
<label htmlFor="color">Select an Option </label>
<Select
id="color"
options={options}
onChange={this.handleChange}
value={this.props.value}
/>
</div>
);
}
}
const MyEnhancedForm = withFormik({
mapPropsToValues: props => ({
myText: "",
option: {}
}),
handleSubmit: (values, { setSubmitting }) => {
console.log(values);
}
})(MyForm);
const App = () => <MyEnhancedForm />;
render(<App />, document.getElementById("root"));
There are a few gotchas, you have to include the react select css for the component to display properly
import "react-select/dist/react-select.css";
the function mapPropsToValues option field should be initialised to an object
mapPropsToValues: props => ({
myText: "",
option: {}
Finally here is a link to the codesandbox example

How to make validation to work for redux-form field generated from an array

I am trying to use redux-form to generate a quiz form. My data source for an individual redux-form field component comes from an array - questions in my case. Everything works as expected except validation. Any thoughts how this can be fixed?
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { Input, Button } from 'reactstrap';
const validate = values => {
const errors = {};
if (!values.question) { // this is just an example of what I am trying to do, validation does not work
errors.question = 'Required';
} else if (values.question.length < 15) {
errors.question = 'Must be 15 characters or more';
}
return errors;
};
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<Input {...input} type={type} />
{touched && (error && <span>{error}</span>)}
</div>
</div>
);
const renderQuestions = questions => {
return questions.map(question => {
return (
<Field key={question.id} name={question.prompt} type="textarea" component={renderField} label={question.prompt} />
);
});
};
const QuizStepForm = props => {
const { handleSubmit, pristine, reset, submitting, questions } = props;
return (
<form onSubmit={handleSubmit}>
<Field name="username" type="textarea" component={renderField} label="username" />
{renderQuestions(questions)}
<div>
<br />
<Button color="primary" style={{ margin: '10px' }} type="submit" disabled={submitting}>
Submit
</Button>
<Button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</Button>
</div>
</form>
);
};
export default reduxForm({
form: 'quizStepForm',
validate
})(QuizStepForm);
Your validation function assumes there is one field named "question." But your code creates a set of fields whose name is set by {question.prompt}. If you stick with this implementation, your validation code will need to know about all the question.prompt array values and check values[question.prompt] for each one, then set errors[question.prompt] for any failures. That would probably work, though it seems like a suboptimal design.
This might be a good use case for a FieldArray. In FieldArrays, the validation function is called for you on each field; your validation code doesn't have to know the names of all the fields.

Categories