Antd form remote submit - reactjs

I want to trigger the submit button outside the antd form
Here is my code
<Form
{...layout}
name="basic"
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
label="Username"
name="username"
rules={[
{
required: true,
message: 'Please input your username!',
},
]}
>
<Input />
</Form.Item>
<Form.Item {...tailLayout}>
// Submit button here works well
</Form.Item>
</Form>
I want this outside form tag, please suggest
<Button type="primary" htmlType="submit">
Submit
</Button>

You can try this way:
const [form] = Form.useForm();
const handleFinish = (values) => {
console.log("values: ", values);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<MyForm form={form} onFinish={handleFinish} />
<Button onClick={() => form.submit()}>Submit</Button>
</div>
);
export const MyForm = ({ form, onFinish }) => {
return (
<Form
form={form}
onFinish={onFinish}
initialValues={{ username: "John Doe" }}
>
<Form.Item name="username">
<Input />
</Form.Item>
</Form>
);
};

If the form and the button are not in the same component then it is hard to create same reference for the form in 2 different place. But I found an other easy way:
<Form id="myForm">...</>
...
<Button form="myForm" key="submit" htmlType="submit">
Submit
</Button>
This is the answer from this issue

write a custom function like this:
function handleSubmit(){
form.validateFields().then((values) => {
// do something with values
})
}
and call it from Button onClick. so you don't need Form onFinish props.

sorry i was searching and see this old question.
as in the comment you should use
const [form] = Form.useForm();
that's a lot of function you can use one of them is submit.
form.submit();
you can refer to document and search for useform:
https://ant.design/components/form/

Related

Setting the value of an input field in antd react JS

I want to set the value of a field based on what I have in the database. Make it ineditable but also want the ability for customers to copy that value. I am able to read the values on submit but I dont know how to set them initially. I am using this
form.setFieldsValue({
clientID: 'value from database',
});
but its not working.
const APIAccess = () => {
const [form] = Form.useForm();
const dbvalues = "some value from database"
form.setFieldsValue({
clientID: dbvalues,
});
const handleSubmit = (value) =>
{
const payload = {
"appName" : form.getFieldValue('appName') ,
}
return (
<Form
name="control-hooks"
form={form}
>
<Form.Item
label="App Name"
name="appName"
rules={[
{
required: true,
message: 'Please select an App Name!',
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="Your Client ID"
name="clientID"
>
<Input.Group >
<Space>
<Input/>
<Tooltip title="copy your Client ID">
<Button icon={<CopyOutlined />} onClick={() => {navigator.clipboard.writeText(clientID)}}/> </Tooltip>
</Space>
</Input.Group>
</Form.Item>
<Form.Item
>
<Button type="primary" htmlType="submit" onClick={handleSubmit}>
Save
</Button>
</Form.Item>
</Form>)
You don't want to programmatically set each initial value. Instead, pass all the initial values to the Form component:
<Form
initialValues={{
clientID: some_client_id,
}}
/>

How to get error message by getFieldError in AntD Form?

I'm validating form with Ant Design but I have a problem with getFieldError(). I need to get the error messages by the field name of Form.Item but it not works.
Here's my code not works below:
...
<Form form={form} name="login_form" onFinish={onFinish} scrollToFirstError>
<Form.Item
label="Password"
name="password"
rules={[
{
required: true,
message: 'Password cannot be empty!',
},
]}
help='' // hide validating message.
>
<>
{({ getFieldError }) => console.log(getFieldError('password'))}
//it not logging anything when submit form trigger error
<Input.Password placeholder="Enter your password" />
</>
</Form.Item>
</Form>
How can I solved this problem?
Your console.log will invoke, every time your component renders, so you need to rerender your component whenever validation applies on the form. To handle this situation you can use render prop with Form component, like this:
function CustomFormItem({ error, ...other }) {
console.log(error)
return <Input.Password placeholder="Enter your password" {...other} />;
}
function MyForm() {
const [form] = Form.useForm();
const onFinish = (values) => {
console.log(values);
};
return (
<Form
form={form}
name="login_form"
onFinish={onFinish}
scrollToFirstError
>
{(values,formInstance) => {
return (
<>
<Form.Item
label="Password"
name="password"
rules={[
{
required: true,
message: 'Password cannot be empty!',
},
]}
help="" // hide validating message.
>
<CustomFormItem error={formInstance.getFieldError('password')} />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</>
);
}}
</Form>
);
}
Consider that with above implementation, render prop will call with every change on the form.
Use can use getFieldError(); like the code example below
Function as Child Component is not working perfectly inside <Form.Item> component as of now.
<Form {...layout} form={form} name="control-hooks" onFinish={onFinish}>
{(values, formInstance) => {
console.log(formInstance.getFieldError("password")[0]);
return (
<>
<Form.Item
name="password"
label="Password"
rules={[
{
required: true
}
]}
>
<Input />
</Form.Item>
</>
);
}}
</Form>

In React with Formik how can I build a search bar that will detect input value to render the buttons?

New to Formik and React I've built a search component that I'm having issues with the passing of the input value and rendering the buttons based on input length.
Given the component:
const SearchForm = ({ index, store }) => {
const [input, setInput] = useState('')
const [disable, setDisable] = useState(true)
const [query, setQuery] = useState(null)
const results = useLunr(query, index, store)
const renderResult = results.length > 0 || query !== null ? true : false
useEffect(() => {
if (input.length >= 3) setDisable(false)
console.log('input detected', input)
}, [input])
const onReset = e => {
setInput('')
setDisable(true)
}
return (
<>
<Formik
initialValues={{ query: '' }}
onSubmit={(values, { setSubmitting }) => {
setInput('')
setDisable(true)
setQuery(values.query)
setSubmitting(false)
}}
>
<Form className="mb-5">
<div className="form-group has-feedback has-clear">
<Field
className="form-control"
name="query"
placeholder="Search . . . . ."
onChange={e => setInput(e.currentTarget.value)}
value={input}
/>
</div>
<div className="row">
<div className="col-12">
<div className="text-right">
<button type="submit" className="btn btn-primary mr-1" disabled={disable}>
Submit
</button>
<button
type="reset"
className="btn btn-primary"
value="Reset"
disabled={disable}
onClick={onReset}
>
<IoClose />
</button>
</div>
</div>
</div>
</Form>
</Formik>
{renderResult && <SearchResults query={query} posts={results} />}
</>
)
}
I've isolated where my issue is but having difficulty trying to resolve:
<Field
className="form-control"
name="query"
placeholder="Search . . . . ."
onChange={e => setInput(e.currentTarget.value)}
value={input}
/>
From within the Field's onChange and value are my problem. If I have everything as posted on submit the passed query doesn't exist. If I remove both and hard code a true for the submit button my query works.
Research
Custom change handlers with inputs inside Formik
Issue with values Formik
Why is OnChange not working when used in Formik?
In Formik how can I build a search bar that will detect input value to render the buttons?
You need to tap into the props that are available as part of the Formik component. Their docs show a simple example that is similar to what you'll need:
<Formik
initialValues={{ query: '' }}
onSubmit={(values, { setSubmitting }) => {
setInput('')
otherStuff()
}}
>
{formikProps => (
<Form className="mb-5">
<div className="form-group has-feedback has-clear">
<Field
name="query"
onChange={formikProps.handleChange}
value={formikProps.values.query}
/>
</div>
<button
type="submit"
disabled={!formikProps.values.query}
>
Submit
</button>
<button
type="reset"
disabled={!formikProps.values.query}
onClick={formikProps.resetForm}
>
</Form>
{/* ... more stuff ... */}
)}
</Formik>
You use this render props pattern to pull formiks props out (I usually call them formikProps, but you can call them anything you want), which then has access to everything you need. Rather than having your input, setInput, disable, and setDisable variables, you can just reference what is in your formikProps. For example, if you want to disable the submit button, you can just say disable={!formikProps.values.query}, meaning if the query value in the form is an empty string, you can't submit the form.
As far as onChange, as long as you give a field the correct name as it corresponds to the property in your initialValues object, formikProps.handleChange will know how to properly update that value for you. Use formikProps.values.whatever for the value of the field, an your component will read those updates automatically. The combo of name, value, and onChange, all handled through formikProps, makes form handing easy.
Formik has tons of very useful prebuilt functionality to handle this for you. I recommend hanging out on their docs site and you'll see how little of your own code you have to write to handle these common form behaviors.

Unable to set `isSubmitting` with Formik

Edit
As it turns out, it was working all along -- the issue was because my handleLogin method was async
New sandbox:
I have a basic Form component. It passes setSubmitting as one of the available methods, and it passes isSubmitting as well. I want to disable the submit button while the form is submitting, but I'm having trouble with this.
Initially, I had a <form> element and I was trying to set setSubmitting(true) in the below part:
<form
onSubmit={(credentials) => {
setSubmitting(true); // <--
handleSubmit(credentials);
}}
>
But this didn't work. So I've tried getting rid of the <form> and changing <Button> to type="button" instead of submit, and I did,
<Button
color="primary"
disabled={isSubmitting}
fullWidth
size="large"
type="button"
variant="contained"
onClick={() => {
setSubmitting(true);
handleLogin(values);
}}
>
Submit
</Button>
But the problem with this, is that in order to do setSubmitting(false) in case of an error is that I have to do this,
onClick={() => {
setSubmitting(true);
handleLogin(values, setSubmitting); // <--
}}
And in addition to this, I have no use for onSubmit={handleLogin}, but if I remove that, Typescript complains.
There's got to be an easier way to accomplish this (without using useFormik).
What can I do here?
Here is the component:
import * as React from "react";
import { Formik } from "formik";
import { Box, Button, TextField } from "#material-ui/core";
const Form = React.memo(() => {
const handleLogin = React.useCallback(async (credentials, setSubmitting) => {
console.log(credentials);
setTimeout(() => {
setSubmitting(false);
}, 2000);
}, []);
return (
<Formik
initialValues={{
email: ""
}}
onSubmit={handleLogin} // removing this line make Typescript complain
>
{({
handleSubmit,
handleChange,
setSubmitting,
isSubmitting,
values
}) => (
<div>
<TextField
fullWidth
label="Email"
margin="normal"
name="email"
onChange={handleChange}
value={values.email}
variant="outlined"
/>
<Box sx={{ my: 2 }}>
<Button
color="primary"
disabled={isSubmitting}
fullWidth
size="large"
type="button"
variant="contained"
onClick={() => {
setSubmitting(true);
handleLogin(values, setSubmitting);
}}
>
Submit
</Button>
</Box>
</div>
)}
</Formik>
);
});
export default Form;
You forget to put the form inside your Formik component
<Formik>
{...}
<form onSubmit={handleSubmit}>
{...}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
</Formik>
so now you can use your button as submit.
demo: https://stackblitz.com/edit/react-egp1gc?file=src%2FForm.js

Formik validate initial values on page load

Code below validate when submit and onFocusOut of textbox fine, What I expect it to trigger validation first reload of the page with initial values.
Tried validateOnMount, as well as others but not worked.
Whats missing here?
const RoleValidationSchema = Yup.object().shape({
Adi: Yup.string()
.min(2, "En az 2 karakter olmalıdır")
.max(30, "En fazla 30 karakter olmalıdır")
.required("Gerekli!")
})
const Role = (props) => {
return (
<div>
<Formik
onSubmit={(values, { validate }) => {
debugger
validate(values);
alert("Submietted!")
props.handleFormSubmit()
}}
initialValues={{
Adi: "d"
}}
validationSchema={RoleValidationSchema}
validateOnMount={true}
validateOnChange={true}
validateOnBlur={true}
render={({ errors, touched, setFieldValue, ...rest }) => {
debugger
return (
<Form>
<Row>
<Col sm="12">
<Label for="Adi">Rol Adi</Label>
<FormGroup className="position-relative">
<Field
autoComplete="off"
id="Adi"
className="form-control"
name="Adi"
type="text"
/>
<ErrorMessage name="Adi">
{(msg) => (
<div className="field-error text-danger">{msg}</div>
)}
</ErrorMessage>
</FormGroup>
</Col>
<Tree dataSource={treeData} targetKeys={targetKeys} expandKeys={[]} onChange={onChange} />
</Row>
<button type="submit" className="btn btn-success" > Kaydet</button>
</Form>
)
}}
/>
</div>
)
}
This worked for me to show validation error on form load.
useEffect(() => {
if (formRef.current) {
formRef.current.validateForm()
}
}, [])
Try to add enableReinitialize Prop to your Formik Component
<Formik
enableReinitialize
......
/>
I had a bit specific case with dynamic Yup schema. Schema was generated based on API data. And validateOnMount was failing as when component was mounted there was no schema yet (API response not received yet).
I end up with something like this.
Inside component I used useEffect. Notice: I used useRef to be able to reference Formik outside of form.
useEffect(() => {
if (formRef.current) {
// check that schema was generated based on API response
if(Object.keys(dynamicValidationSchema).length != 0){
formRef.current.validateForm()
}
}
}, [initialData, dynamicValidationSchema])
Formik:
<Formik
innerRef={formRef}
enableReinitialize
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={props.handleSubmit}
>
It's possible to provide a initial value for validation in the "initialIsValid" prop.
const validationSchema= Yup.object()
const initialValues = { ... }
const initialIsValid = schema.isValidSync(initialValues)
return <Formik
initialValues={initialValues}
validationSchema={validationSchema}
initialIsValid={initialIsValid }
...
>
...
</Formik>
The only solution I found is to explicitly add initialErrors={{..}} to Formik.
In the below example, it's added depending on some condition. Unfortunately that duplicates the Yup validation/message which I also have in the schema. Nothing else worked, no validateOnMount or anything else.
<Formik
enableReinitialize
validationSchema={schema}
onSubmit={ (values) => {
submitForm(values);
}}
initialValues={initialFormValues}
/* Unfortunately a duplication of Yup Schema validation on this field,
but can't force Formik/Yup to validate initially */
initialErrors={!isEmptyObject(initialFormValues) &&
initialFormValues.inactiveApproverCondition &&
{'approverId': 'Selected Approver is no longer eligible. Please choose a different Approver to continue.'}}
>
You should try updating your code follow this
render={({ errors, touched, setFieldValue, isValid, ...rest })
and
<button type="submit" className="btn btn-success" disabled={!isValid}> Kaydet</button>

Resources