remove hidden field from final json after submit using Formik - javascript

I build a simple form using formik. I have a situation that one field is hidden after I choose in radio buttun 'no'.
But if I enter value to this field and then choose 'no' to hide it, and submit the form I will get in the final json also the value of the hidden field.
My expectation is when field isn't show to the screen, remove it from the final json.
My form:
import React from "react";
import { Formik } from "formik";
import { Form } from "react-bootstrap";
import { Radio } from "antd";
const CustomFormik = () => {
return (
<>
<Formik
initialValues={{
input1: null,
radio: null,
input2: null
}}
onSubmit={(values) => {
console.log(values);
}}
>
{(formik) => (
<>
<Form
onSubmit={formik.handleSubmit}
className="custom-formik-container"
>
<Form.Group>
<span>input1: </span>
<Form.Control
id={"input1"}
name={"input1"}
{...formik.getFieldProps("input1")}
/>
</Form.Group>
{formik.getFieldProps("radio").value !== 2 && (
<Form.Group>
<span>input2: </span>
<Form.Control
id={"input2"}
name={"input2"}
{...formik.getFieldProps("input2")}
/>
</Form.Group>
)}
<Form.Group>
<span>radio: </span>
<Radio.Group {...formik.getFieldProps("radio")}>
<Radio value={1}>yes</Radio>
<Radio value={2}>no</Radio>
</Radio.Group>
</Form.Group>
</Form>
<button
style={{
width: 200,
height: 30,
background: "#113776",
color: "#fff"
}}
onClick={() => formik.handleSubmit()}
>
S U B M I T
</button>
</>
)}
</Formik>
</>
);
};
export default CustomFormik;
For example enter:
'abc' to the first input.
'efg' to the second input.
'no' to the radio button.
The final json in console.log will be:
{input1: "abc",input2: "efg", radio: 2}
But my expectation in to be:
{input1: "abc", radio: 2}
codesandbox
Thank you guys!

I don't know if this fits your needs but I had a similar case in my project using redux-form and what i did was to delete the property before submiting it
sth like this
onSubmit={(values) => {
if (values.radio === 2) {
let clonedValues = { ...values };
delete clonedValues.input2;
console.log(clonedValues);
} else {
console.log(values);
}
}}

this will do the job.
onSubmit={(values) => {
if(values.radio === 2){
delete values['input2'];
}
console.log(values);
}}

Related

Formik not submitting when clicking submit button. (React and NextJS)

The form input campaignid is automatically filled in when the page is loaded.
I've added onChange and onBlur to name input to prevent default, however when you click on the submit button, no action is taken.
I'm using a third party script to get URL parameters and insert them into campaignid value.
import * as React from "react";
import Script from "next/script";
import { Field, Form, Formik } from "formik";
import {
Button,
Flex,
FormControl,
FormErrorMessage,
FormLabel,
Input,
} from "#chakra-ui/react";
export default function FormikForm() {
function validateName(value) {
let error;
if (!value) {
error = "Type a valid name.";
}
return error;
}
return (
<>
<Script src="https://cdn.jsdelivr.net/gh/gkogan/sup-save-url-parameters/sup.min.js" />
<Formik
initialValues={{
name: "",
}}
onSubmit={(values) => {
setTimeout(() => {
fetch(`https://hooks.zapier.com/hooks/catch/3660927/bte5w75/`, {
method: "POST",
body: JSON.stringify(values, null, 2),
}),
3000;
});
}}
>
{(props) => (
<Flex>
<Form id="hero">
<Field name="name" validate={validateName}>
{({ field, form }) => (
<FormControl
isInvalid={form.errors.name && form.touched.name}
>
<FormLabel htmlFor="name">Nome</FormLabel>
<Input
{...field}
id="name"
onChange={(e) => {
e.preventDefault();
}}
onBlur={(e) => {
e.preventDefault();
}}
/>
<FormErrorMessage>{form.errors.name}</FormErrorMessage>
</FormControl>
)}
</Field>
<Field name="campaignid">
{({ field }) => (
<FormControl isReadOnly>
<Input {...field} type="hidden" id="campaignid" />
</FormControl>
)}
</Field>
<Button
id="submited"
isLoading={props.isSubmitting}
type="submit"
>
Submit
</Button>
</Form>
</Flex>
)}
</Formik>
</>
);
}

Antd form initial value not updating after state change

I have a table showing school name and its fee or rates. There is button in table which opens a drawer containing this component or form.There is a button in table which toggles the state of drawer. As long as i am only reading values in the form by clicking on the button my initalValue updates. But if i update price and submit form for any 1 entry , then initial value my price or school doesn't change for other entries or schools. While my state is changing accordingly which I did check by rendering state. But my initialValue of form doesn't updating accordingly
import React, { useState, useEffect } from 'react'
import { Table, Button, Popconfirm, Drawer, Form, Input, Select, notification } from 'antd'
const { Option } = Select
const layout = {
labelCol: {
span: 10,
},
wrapperCol: {
span: 10,
},
}
const tailLayout = {
wrapperCol: {
offset: 7,
span: 14,
},
}
function SchoolRates({ form, relevantData }) {
const [rates, setRates] = useState(null)
useEffect(() => {
setRates(relevantData)
}, [someCondition])
const updateRates = e => {
e.preventDefault()
form.validateFields((error, values) => {
console.log(error, values, 'forms')
})
return 'Data Updated'
}
return (
<>
<div>
{`${rates?.schoolName}, ${rates?.Price}`}
</div>
<Form {...layout} initialValue={rates} onSubmit={e => updateRates(e)}>
<Form.Item label="Country" style={{ marginBottom: '5px' }} size="small">
{form.getFieldDecorator('schoolName', {
initialValue: rates?.schoolname,
rules: [{ required: true, message: 'Please select a school!' }],
})(
<Select
placeholder="School"
allowClear
size="small"
style={{ borderRadius: 0 }}
>
{schoolList.map(item => (
<Option value={item}>{item}</Option>
))}
</Select>,
)}
</Form.Item>
<Form.Item label="Price / Learner" style={{ marginBottom: '5px' }} size="small">
{form.getFieldDecorator('price', {
initialValue: rates?.price,
rules: [{ required: true, message: 'Please enter price/learner' }],
})(
<Input
size="small"
type="number"
style={{ borderRadius: 0 }}
/>,
)}
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit" size="small">
Save
</Button>
</Form.Item>
</Form>
</>
)
}
export default Form.create()(UpdateRates)
I just want to re render the complete form to update data as soon as I click on other school from my table
To update initialValue form input fields need to be cleaned or reset.
So add form.resetFields()(to reset data in input fields) after successful submission of form.

How to use Formik ErrorMessage properly with formik using render props and design library for inputs?

I want to trigger formik errors and touched whenever the input is clicked and the value is not correct .
I pass formik props to the input component like this :
const initialValues = {
title: ''
};
const validationSchema = yup.object({
title: yup.string().max(50, 'less than 50 words !!').required('required !!')
});
function Add() {
<Formik initialValues={initialValues} onSubmit={onSubmit} validationSchema={validationSchema}>
{(props) => {
return (
<Form>
<AddTitle props={props} />
</Form>
);
}}
</Formik>
);
}
Here I'm trying to display error message whenever the input is touched and there is an error with it like this :
import { Input } from 'antd';
function AddTitle(props) {
console.log(props.props);
return (
<Field name="title">
{() => {
return (
<Input
onChange={(e) => {
props.props.setFieldValue('title', e.target.value)
}}
/>
);
}}
</Field>
<ErrorMessage name="title" />
<P>
{props.props.touched.title && props.props.errors.title && props.props.errors.title}
</P>
</React.Fragment>
);
}
But ErrorMessage and the paragraph below it doesn't work when the input is touched and empty .
In console log it shows that input doesn't handle formik touched method and it only triggers the error for it :
touched:
__proto__: Object
errors:
title: "less than 50 words !"
__proto__: Object
How can I use ErrorMessage properly while passing in formik props to a component and using a third library for inputs ?
Fixed the issue by adding the onBlur to the input and ErrorMessage is working fine :
<Field name="title">
{() => {
return (
<Input
onBlur={() => props.props.setFieldTouched('title')}
onChange={(e) => {
props.props.setFieldValue('title', e.target.value);
}}
/>
);
}}
</Field>
<P class="mt-2 text-danger">
<ErrorMessage name="title" />
</P>

How to define setFieldValue in React

I'm using Formik for validating the fields before creating an entity. There is a Select which opens a dropdown and the user must choose one option from there.
I get an error saying that setFieldValue is not defined but where I've researched before I didn't find any definition of this method, it is just used like that so I don't know why is this happening.
This is my code:
import React from 'react';
import { Formik, Form, Field } from 'formik';
import { Button, Label, Grid } from 'semantic-ui-react';
import * as Yup from 'yup';
class CreateCompanyForm extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
industry: '',
};
}
onChange = (e, { name, value }) => {
this.setState({ [name]: value });
};
handleSubmit = values => {
// ...
};
render() {
const nameOptions = [
{ text: 'Services', value: 'Services' },
{ text: 'Retail', value: 'Retail' },
];
const initialValues = {
industry: '',
};
const requiredErrorMessage = 'This field is required';
const validationSchema = Yup.object({
industry: Yup.string().required(requiredErrorMessage),
});
return (
<div>
<div>
<Button type="submit" form="amazing">
Create company
</Button>
</div>
<Formik
htmlFor="amazing"
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={values => this.handleSubmit(values)}>
{({ errors, touched }) => (
<Form id="amazing">
<Grid>
<Grid.Column>
<Label>Industry</Label>
<Field
name="industry"
as={Select}
options={nameOptions}
placeholder="select an industry"
onChange={e => setFieldValue('industry', e.target.value)} // here it is
/>
<div>
{touched.industry && errors.industry
? errors.industry
: null}
</div>
</Grid.Column>
</Grid>
</Form>
)}
</Formik>
</div>
);
}
}
The error says: 'setFieldValue' is not defined no-undef - it is from ESLint. How can be this solved?
setFieldValue is accessible as one of the props in Formik.
I tried it on CodeSandbox, apparently, the first parameter in the onChange prop returns the event handler while the second one returns the value selected onChange={(e, selected) => setFieldValue("industry", selected.value) }
<Formik
htmlFor="amazing"
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values) => this.handleSubmit(values)}
>
{({ errors, touched, setFieldValue }) => (
//define setFieldValue
<Form id="amazing">
<Grid>
<Grid.Column>
<Label>Industry</Label>
<Field
id="industry" // remove onBlur warning
name="industry"
as={Select}
options={nameOptions}
placeholder="select an industry"
onChange={(e, selected) =>
setFieldValue("industry", selected.value)
}
/>
<div>
{touched.industry && errors.industry
? errors.industry
: null}
</div>
</Grid.Column>
</Grid>
</Form>
)}
</Formik>
try calling it using this structure
onChange = {v => formik.setFieldValue('field', v)}
<Formik
htmlFor="amazing"
initialValues={initialValues}
value={{industry:this.state.industry}}
validationSchema={validationSchema}
onSubmit={(values) => this.handleSubmit(values)}
>
{({ errors, touched }) => (
<Form id="amazing">
<Grid>
<Grid.Column>
<Label>Industry</Label>
<Field
name="industry"
as={Select}
options={nameOptions}
placeholder="select an industry"
onChange={(e) =>
this.setState({
industry: e.target.value,
})
} // here it is
/>
<div>
{touched.industry && errors.industry ? errors.industry : null}
</div>
</Grid.Column>
</Grid>
</Form>
)}
</Formik>;
Replace your onChange by onChange={this.onChange('industry', e.target.value)}
and in your constructor add this line this.onChange = this.onChange.bind(this).
your onChange methode will be:
onChange(e, { name, value }){
this.setState({ [name]: value });
}

Is it possible to handle several forms by one submit button?

I have a project on ReactJS which you can find here (see develop-branch) or check it out on our web site.
As you can see, I use formik to handle forms.
Now I have only one submit button which handles all forms however it does not link with forms by form attribute. It was OK.
Unfortunately, I've faced a problem when having a go to implement form validation. I still prefer using formik validation, but the thing is that it demands a direct connection between form and submit button like this:
export function GenerateButton(props) {
return (
<Button id="genButton"
form="form1"
type="submit"
onClick={props.onClick}>
Generate
</Button>
);
}
Any ideas how I can link all forms with submit button?
Or I have to just use fictitious buttons in every form (position: absolute; left: -9999px;) and imitate their click after pushing generate button?
P.S. now there is id="forms" in html form tag, it is just stupid mistake, must be class attribute. I can generate unique id this way: id={"form"+(props.index + 1)}.
P.S.S. I am so sorry for my English.
I think you can handle this with fieldarray of Formik very easily.
you will have an array and a list of forms in it then you can simply add and remove forms.
you won't have any problem with using validation of Formik too.
here is an example that exactly does what you want:
import React from "react";
import { Formik, Form, Field, FieldArray } from "formik";
const formInitialValues = { name: "", lastName: "" };
const FormList = () => (
<Formik
initialValues={{ forms: [formInitialValues] }}
onSubmit={values =>
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 500)
}
render={({ values }) => (
<Form>
<FieldArray
name="forms"
render={arrayHelpers => (
<div>
{values.forms.map((formItem, index) => (
<div key={index}>
<Field name={`forms.${index}.name`} />
<Field name={`forms.${index}.lastName`} />
<button
type="button"
onClick={() => arrayHelpers.remove(index)} // remove a form from the list of forms
>
-
</button>
<button
type="button"
onClick={() =>
arrayHelpers.insert(index, formInitialValues)
} // insert an empty string at a position
>
+
</button>
</div>
))}
<div>
<button type="submit">Submit</button>
</div>
</div>
)}
/>
</Form>
)}
/>
);
export default FormList;
I have provided a code sandbox version for you too
please let me know if you still have any problem
more reading:
https://jaredpalmer.com/formik/docs/api/fieldarray#fieldarray-array-of-objects
The fieldArray solution looks quite complex and I think useFormik is a better option here.
For example:
import React from 'react'
import { Button, Grid, TextField } from '#mui/material'
import clsx from 'clsx'
import { useFormik } from 'formik'
export function MyFormComponent(props: any) {
const formA = useFormik({
initialValues: {
age: 23
},
onSubmit: values => {
alert(JSON.stringify(values, null, 2))
}
})
const formB = useFormik({
initialValues: {
name: 'bar'
},
onSubmit: values => {
alert(JSON.stringify(values, null, 2))
}
})
const formC = useFormik({
initialValues: {
description: 'A flat object'
},
onSubmit: values => {
alert(JSON.stringify(values, null, 2))
}
})
const submitForm = () => {
//get your values here
const values1 = formA.values
const values2 = formB.values
const values3 = formC.values
//data access for storing the values
}
return (
<div>
<form onSubmit={formA.handleSubmit}>
<TextField
style={{ marginBottom: 20 }}
fullWidth
type='number'
label='Age'
id='age'
name='age'
InputProps={{
inputProps: {
min: 0
}
}}
onChange={formA.handleChange}
value={formA.values.age}
/>
</form>
<form onSubmit={formB.handleSubmit}>
<Grid container spacing={2}>
<Grid item xs={6}>
<TextField
onChange={formB.handleChange}
value={formB.values.name}
style={{ marginRight: 20 }}
fullWidth
name='name'
type='text'
label='Name'
InputProps={{
inputProps: {
min: 0
}
}}
/>
</Grid>
</Grid>
</form>
<form onSubmit={formC.handleSubmit}>
<Grid container spacing={2}>
<Grid item xs={6}>
<TextField
onChange={formC.handleChange}
value={formC.values.description}
style={{ marginRight: 20 }}
fullWidth
name='description'
type='text'
label='Description'
InputProps={{
inputProps: {
min: 0
}
}}
/>
</Grid>
</Grid>
</form>
<Button color='secondary' variant='contained' onClick={submitForm}>
Submit
</Button>
</div>
)
}
I'm created a special utility library to work with multiple forms (including repeatable forms) from one place. I see that you are using Formik component (instead of useFormik hook), but maybe it still be useful.
Check this example
https://stackblitz.com/edit/multi-formik-hook-basic-example?file=App.tsx
You can find the library here

Categories