Antd with formik setFieldValue - javascript

There is a Antd table with data, when i click to button "Change", the entire line with data is passed to the function in the form of an object, now it remains to insert data into the fields, but I can't, what should I do?
How set data to antd input using outside function?
I can do it with .map (), but it seems more appropriate to me to use formik
const BankForm = (props) => {
const [form] = Form.useForm();
//show close form
const columns = [
...MY columns
{
title: "Действие",
key: "action",
fixed: "right",
render: (text, record) => (
<Space size="middle"
I clink to Button and all data of row go to ChangeBank(record)
<a onClick={() => ChangeBank(record)}> Изменить</a>
</Space>
),
},
];
const formik = useFormik({
initialValues: {
name_bank: "",
},
validate,
onSubmit: (values) => {
},
});
//my function to set data to form
let ChangeBank = (record) => {
formik.setFieldValue("name_bank", record.name_bank);
};
return (
<div className={clas.tableBlock_header}>
my form
<Form
form={form}
layout="vertical"
hideRequiredMark
onFinish={formik.handleSubmit}
>
<Form.Item
name="name_bank"
label="Название банка"
rules={[{ required: true, message: "Введите название банка" }]}
>
<Input value={formik.values.name_bank} name="name_bank" />
</Form.Item>
</Form>
);
};

If you want to set values in antd form fields with formik, dont give the name Form.item
AND if you want use antd with formik, and you need validaite your inputs just download Yup. Dont use antd menthods.
Yup with formik
<Form
form={form}
layout="vertical"
hideRequiredMark
onFinish={formik.handleSubmit}
>
<Form.Item
label="Название банка"
rules={[{ required: true, message: "Введите название банка" }]}
>
<Input value={formik.values.name_bank} name="name_bank" />
</Form.Item>
</Form>**strong text**

Related

Unable to edit a post's tags properly [React JS] due to an uncaught type error

new to React and was following along with a tutorial online
I ran into an issue when editing a post, namely, I could edit all the other fields like "title", "creator", "message", but the site wouldn't load when I updated the tag--I'd have to manually refresh the page to see any changes.
Here's the issue-causing code that I had originally:
<Typography variant="body2" color="textSecondary">{post.tags.map((tag) => `#${tag} `)}</Typography>
As you can see, I wanted to enter something like "tag1, tag2, tag3" in the tags field and have it displayed on the post as "#tag1 #tag2 #tag3". However, my console said that post.tags.map is not a function. However, this was used successfully in the tutorial, does this function no longer work?
When I refresh the page, the tags are successfully updated. However, before refreshing, all the components of the page aside from the background css vanishes.
For the sake of just getting the edit function to work, I changed it to:
<Typography variant="body2" color="textSecondary">{post.tags}</Typography>
Which works, but just displays the tags as "tag1 tag2 tag3" without the hashtags.
Again, this is not a big deal, but I am confused as to why post.tags.map did not work. Again, I am new to JS so I might have done something silly.
Here is the code for the postMessage model:
const postSchema = mongoose.Schema({
title: String,
message: String,
creator: String,
tags: [String],
selectedFile: String,
likeCount: {
type: Number,
default: 0
},
createdAt: {
type: Date,
default: new Date()
}
});
On top of asking why post.tags.map does not work, how would I get each tag to be displayed with a hashtag before it without having to manually type the hashtag in the field? Currently, with my temporary "fix", all the commas also are displayed on the post, which I assume is because it's putting all the tags as a single string element of the array.
Some additional information that might be helpful:
The site only acts strange when editing a post's tags, not when creating a post with tags. This makes me wonder if there's some funny business with any updatePost functions--however, if that were the case, that would mean I wouldn't be able to update any of the other fields either, which I can.
Here's my code for the Form:
import React, { useState, useEffect } from 'react';
import { TextField, Button, Typography, Paper } from '#material-ui/core';
import FileBase from 'react-file-base64';
import { useDispatch, useSelector } from 'react-redux';
import useStyles from './styles';
import { createPost, updatePost } from '../../actions/posts';
// get current ID
const Form = ({ currentId, setCurrentId }) => {
const [postData, setPostData] = useState({
creator: '', title: '', message: '', tags: '', selectedFile: ''
});
const post = useSelector((state) => currentId ? state.posts.find((p) => p._id === currentId) : null);
const classes = useStyles();
const dispatch = useDispatch();
useEffect(() => {
if(post) setPostData(post);
}, [post]);
const handleSubmit = (e) => {
e.preventDefault();
if(currentId) {
dispatch(updatePost(currentId, postData));
} else {
dispatch(createPost(postData));
}
clear();
}
const clear = () => {
setCurrentId(null);
setPostData({creator: '', title: '', message: '', tags: '', selectedFile: ''});
}
return (
<Paper className={classes.paper}>
<form autoComplete='off' noValidate className={`${classes.root} ${classes.form}`} onSubmit={handleSubmit}>
<Typography variant="h6">{ currentId ? 'Editing' : 'Creating' } a Post </Typography>
<TextField name="creator" variant="outlined" label="Creator" fullWidth value={postData.creator} onChange={(e) => setPostData({ ...postData, creator: e.target.value })} />
<TextField name="title" variant="outlined" label="Title" fullWidth value={postData.title} onChange={(e) => setPostData({ ...postData, title: e.target.value })} />
<TextField name="message" variant="outlined" label="Message" fullWidth value={postData.message} onChange={(e) => setPostData({ ...postData, message: e.target.value })} />
<TextField name="tags" variant="outlined" label="Tags" fullWidth value={postData.tags} onChange={(e) => setPostData({ ...postData, tags: e.target.value })} />
<div className={classes.fileInput}>
<FileBase type="file" multiple={false} onDone={({base64}) => setPostData({ ...postData, selectedFile: base64})}/>
</div>
<Button className={classes.buttonSubmit} variant="contained" color="primary" size="large" type="submit" fullWidth>Submit</Button>
<Button variant="contained" color="secondary" size="small" onClick={clear} fullWidth>Clear</Button>
</form>
</Paper>
);
}
export default Form;
And, just to be thorough, my code in actions/posts.js:
export const updatePost = (id, post) => async (dispatch) => {
try {
const { data } = await api.updatePost(id, post);
dispatch({ type: 'UPDATE', payload: data });
} catch (error) {
console.log(error);
}
}
If there is any other bits of code you'd need to see, let me know and I'll update this post.
Thanks

Rendering text of one input field to another in Ant Design

I am using ant design and I have 2 input fields Enter Example and Select Value now when I select text from Enter Example the selected text should get rendered inside the Select Value field
For selecting the highlighted text I am using window.getSelection().toString() and when I console I am getting the selected text value but how can I display that inside the Select Value Input Field
Problem :- How to render the selected text of Enter Example Input Field inside Select Value Input Field
What I have tried :-
const ExampleComponent = (props) => {
const [exampleValue,setExampleValue] = useState('')
const handleMouseUp = () => {
const selectedTextValue = window.getSelection().toString()
setExampleValue(selectedTextValue)
console.log(selectedTextValue)
}
return (
<Form
form={form}
labelCol={{ span: 7 }}
wrapperCol={{ span: 10 }}
layout="horizontal"
colon={true}
onFinish={onFinish}
size="large"
>
<Form.Item
label="Enter Example"
name="example_name"
className="csi-ant-form-item"
hasFeedback
rules={[
{ required: true, message: "This Field Cannot be Empty!" },
({ getFieldValue }) => ({
validator(_, value) {
if (value.length < 4) {
return Promise.reject("Length too short");
}
return Promise.resolve();
},
}),
]}
>
<AutoComplete>
<Input allowClear onMouseUp={handleMouseUp}/>
</AutoComplete>
</Form.Item>
<Form.Item
label="Select Value"
name="entity_value"
className="csi-ant-form-item"
hasFeedback
rules={[
{ required: true, message: "This Field Cannot be Empty!" },
]}
>
<AutoComplete>
<Input placeholder="Select Value from Example" value={exampleValue}/>
</AutoComplete>
</Form.Item>
</Form>
)
}
I am trying to display the highlighted text using the value prop like value ={exampleValue}

save data from many forms using getFieldValue()

I try to save data from 2 form (Main and SubForm) using getFieldValue(). Here should appear both form data:
const save = () => {
console.log(myRef.current.getFieldValue());
};
Now i get an empty object when i click save handler. If i remove the <SubForm/> i get value from Main form, but if i add again the second form i ca not get data from both form.
,br> How to get data, clicking on save button, from both forms using getFieldValue()?
demo: https://codesandbox.io/s/dazzling-ganguly-vz7o7?file=/src/OuterForm.js
A part of my code:
<Form
name="main"
ref={myRef}
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
id="ma"
form={form}
>
<Form.Item
label="Username"
name="username"
rules={[{ required: true, message: "Please input your username!" }]}
>
<Input />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[{ required: true, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
</Form>
<SubForm myRef={myRef} />
<button onClick={save}>save</button>
</div>
You need to define 2 refs, one for each form.
const mainFormRef = useRef();
const subFormRef = useRef();
and set these refs on respective forms
<Form
name="main"
ref={mainFormRef}
....
>
....
</Form>
<SubForm myRef={subFormRef} />
finally, inside save function, call getFieldValue on both refs
const save = () => {
console.log(mainFormRef.current.getFieldValue());
console.log(subFormRef.current.getFieldValue());
};

How to take values from Formik inside a hook?

I am using Formik in my app. I need to take values from Formik and use it inside my hook.
Currently I am doing it using useRef hook
Following is my custom hook code which requires Formik values
const { doRequest, errors } = useRequest({
url: "my_url",
method: "post",
body: {
mobileNumber: formikRef.current
? formikRef.current.values.mobileNumber
: "",
password: formikRef.current ? formikRef.current.values.password : "",
},
onSuccess: (response) => {
console.log("Hi" + response.msg);
Router.push("/");
},
onFailure: (response) => {
console.log("Hi2" + response.msg);
},
});
In the body, the mobile number and password is always empty even though I enter values in my textfield. I am calling the doRequest method inside onSubmit of Formik
I am asking the ref using following
<Formik
innerRef={formikRef}..
If I had used useState for all my fields It would have been extremely easy to pass those values to my custom hook but due to large form and validation , I am using Formik
You don't need to use innerRef. You can simply use formik values.
Your custom hook should return doRequest which accepts a param so that it can catch dynamic values.
write the hook in such a way that it caters to both static values and dynamic values. See the closed github issue here
working demo
Code snippet
export default () => {
const { doRequest, errors } = useRequest({
url: "my_url",
method: "post",
body: {
mobileNumber: "0000",
password: "xxxx"
},
onSuccess: response => {
console.log("Hi" + response.msg);
// Router.push("/");
},
onFailure: response => {
console.log("Hi2" + response.msg);
}
});
const handleSubmit = (values, actions) => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
doRequest(values);
};
return (
<div className="col-sm-12">
<h1>My Form</h1>
<Formik initialValues={{ mobileNumber: "" }} onSubmit={handleSubmit}>
{({ errors }) => (
<Form>
<Field name="mobileNumber" placeholder="Mobile Number" />
<Field name="password" placeholder="Password" />
<div className="messages-wrapper">
{errors.email && (
<div className="alert alert-danger">
<ErrorMessage name="mobileNumber" />
</div>
)}
{errors.password && (
<div className="alert alert-danger">
<ErrorMessage name="password" />
</div>
)}
</div>
<button className="btn btn-primary" type="submit">
Submit
</button>
</Form>
)}
</Formik>
</div>
);
};
You can get any value from the Formik bag via the useFormikContext() hook. Like so:
const { values } = useFormikContext();
const { mobileNumber } = values;
You could use the predefined useFormikContext() hook provided by Formik or you could set a ref to Formik and use its current value to fetch Formik form values.

Identifying what item have been deleted (created and modifed) in a Formik FieldArray

Was wondering if Formik has a native solution for identifying the addition and deletion (and update) of FieldArray in the form ?
I have the code on sandbox here https://codesandbox.io/s/jn7x2m75o9 ( based on the original Formik Array example # https://github.com/jaredpalmer/formik/blob/master/examples/Arrays.js )
but also the relevant part here :
With an Initial state of 3 friend defined, how can I know in my onSubmithandler which one were modified,deleted,updated.
import React from "react";
import { Formik, Field, Form, ErrorMessage, FieldArray } from "formik";
const initialValues = {
friends: [
{
name: "Friend_A",
email: "email_A#somewhere.com"
},
{
name: "Friend_B",
email: "email_B#somewhere.com"
},
{
name: "Friend_C",
email: "email_C#somewhere.com"
}
]
};
const mySubmit = values => console.log();
const SignIn = () => (
<div>
<h1>Invite friends</h1>
<Formik
initialValues={initialValues}
onSubmit={values => {
var itemRemoved = values.GetItemRemoveFromArray; // This is what I'm looking for
console.log(itemRemoved);
// Would print Friend_A
var itemAdded = values.GetItemAddedFromArray; // This is what I'm looking for
console.log(itemAdded);
// Would print New_Friend
var itemUpdated = values.GetItemUpdatedInArray; // This is what I'm looking for
console.log(itemUpdated);
// Would print Friend_C
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 500);
}}
render={({ values }) => (
<Form>
<FieldArray
name="friends"
render={({ insert, remove, push }) => (
<div>
{values.friends.length > 0 &&
values.friends.map((friend, index) => (
<div className="row" key={index}>
<div className="col">
<label htmlFor={`friends.${index}.name`}>Name</label>
<Field
name={`friends.${index}.name`}
placeholder="Jane Doe"
type="text"
/>
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<label htmlFor={`friends.${index}.email`}>Email</label>
<Field
name={`friends.${index}.email`}
placeholder="jane#acme.com"
type="email"
/>
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<button
type="button"
className="secondary"
onClick={() => remove(index)}
>
X
</button>
</div>
</div>
))}
<button
type="button"
className="secondary"
onClick={() => push({ name: "", email: "" })}
>
Add Friend
</button>
</div>
)}
/>
<button type="submit">Invite</button>
</Form>
)}
/>
</div>
);
export default SignIn;
So if with the above a user where to :
Click on the X below Friend_A
Modify Friend_C email to email_C#nothere.com
Click "Add Friend"
Enter value Name: New_Friend_X and email: XX#YY.com
Click "Add Friend"
Enter value Name: New_Friend_Z and email: Friend_Z#coolplace.com
Click "X" button below newly entered "New_Friend_X"
Click "Invite"
in my mySubmit I'm looking for a way to easily get :
Friend_A was Removed
Friend_C was Modified
New_Friend_Z was added (was not in the original initialValues to formik)
(I Don't care about New_Friend_X. No need to know it was added/removed )
Point of this is to minimize rest call to the back end to create/update entity/link and also I really dont want to write my own "secondary state" in the onClick handler of the remove button before calling the remove(index) handler provided by Formik to track what need to be deleted from the DB.
Its not built into Formik, but it is not hard to do in javascript.
First, understand that Formik clones the object you give to initialValues. So in onSubmit, you will compare the final value to your original object.
The incoming data:
const initialFriends = [
{
name: "Friend_A",
email: "email_A#somewhere.com"
},
{
name: "Friend_B",
email: "email_B#somewhere.com"
},
{
name: "Friend_C",
email: "email_C#somewhere.com"
}
];
const initialValues = { friends: initialFriends };
Modified Formik declaration:
<Formik initialValues={initialValues}
...
onSubmit={values => {
const { added, deleted, changed } = addDeleteChange(
initialFriends,
values.friends
);
setTimeout(() => {
alert(
"Added: " + JSON.stringify(Object.fromEntries(added.entries()))
);
alert(
"Deleted: " + JSON.stringify(Object.fromEntries(deleted.entries()))
);
alert(
"Changed:" + JSON.stringify(Object.fromEntries(changed.entries()))
);
alert(JSON.stringify(values, null, 2));
}, 500);
}}
...
Helper functions:
function partition(array, filter) {
let pass = [],
fail = [];
array.forEach(e => (filter(e) ? pass : fail).push(e));
return [pass, fail];
}
const addDeleteChange = (in1, out1) => {
let inMap = new Map(in1.map(f => [f.name, f]));
let outMap = new Map(out1.map(f => [f.name, f]));
let inNames = new Set(inMap.keys());
let outNames = new Set(outMap.keys());
let [kept, added] = partition(out1, f => inNames.has(f.name));
let deleted = in1.filter(f => !outNames.has(f.name));
//alert(JSON.stringify(Object.fromEntries(deleted.entries())));
let changed = kept.filter(f => f.email !== inMap.get(f.name).email);
//alert(JSON.stringify(Object.fromEntries(changed.entries())));
return { added: added, deleted: deleted, changed: changed };
};
Code in codesandbox
NOTE: If you change the name of a friend, that will appear as a delete of original friend and an add of a new friend.
A more robust solution would be to add a (hidden) "id" field to each friend. Then instead of comparing name, would compare id.
That requires generating a new id as add each friend.

Categories

Resources