i am trying to create a dynamic form where a user can enter the details of a guest list. I also want to be on every row a button called "email" where the user can click on it and an automatically generated email would be sent to the guest on that row. i have created the dynamic form and i have a fun a function that sends emails using (mailjs) what i cant do is pass the details of that particular row to the onlcick function so the email is sent to the right guest
my form code :
const useStyles = makeStyles((theme)=>({
root:{
"& .MuiTextField-root":{
margin: theme.spacing(1),
}
},
button:{
margin: theme.spacing(1)
}
}))
function Guests () {
const classes=useStyles()
const [inputFields, setInputField] = useState([
{firstName: "", lastName:"", email:"", rsvp:"", menue:""},
])
const handleSubmit =(e) =>{
e.preventDefault();
console.log("input fields", inputFields)
}
const handleChangeInput = (index, event) =>{
console.log(index, event.target.name)
const values =[...inputFields];
values[index][event.target.name] = event.target.value
setInputField(values)
}
const handleAddField = () =>{
setInputField([...inputFields,{firstName: "", lastName:"", email:"", rsvp:"", menue:""}])
}
const handleRemoveField =(index) =>{
const values = [...inputFields]
values.splice(index, 1)
setInputField(values)
}
return (
<Container>
<h1> Your Guest List</h1>
<form className={classes.root} onSubmit={handleSubmit}>
{inputFields.map((inputField, index)=>(
<div key={index}>
<TextField
name="firstName"
label="First Name"
value={inputField.firstName}
onChange={event => handleChangeInput(index, event)}
/>
<TextField
name="lastName"
label="Last Name"
value={inputField.lastName}
onChange={event => handleChangeInput(index, event)}
/>
<TextField
name="email"
label="Email"
value={inputField.email}
onChange={event => handleChangeInput(index, event)}
/>
<TextField
name="rsvp"
label="RSVP"
value={inputField.rsvp}
onChange={event => handleChangeInput(index, event)}
/>
<TextField
name="menue"
label="Menu Choice"
value={inputField.menue}
onChange={event => handleChangeInput(index, event)}
/>
<Button className={classes.button}
vareient="contained"
color="secondary"
>Send email</Button>
<IconButton
onClick={()=> handleRemoveField(index)}>
<RemoveIcon />
</IconButton>
<IconButton
onClick={() => handleAddField()}>
<AddIcon />
</IconButton>
</div>
))}
<Button
className={classes.button}
vareient="contained"
color="secondary"
type="submit"
onClick={handleSubmit}
endIcon={<Icon>save</Icon>}>
Save Guest List
</Button>
</form>
</Container>
)
}
export default Guests
my email function is :
function sendEmail (event){
event.preventDefault();
emailjs.sendForm(service,template,event.target,hero).then(res=>{
console.log(res)
alert("email sent thank you");
}).catch(err=> console.log(err));
}
Many thanks for you help
Related
I want to create dynamic form with react-hook-form.
Below is my code.
I want to create a dialog for entering a detailed profile in a separate component and use it in MainForm.
I can display the DetailForm, but the values entered in it are not reflected.
Data on the DetailForm component is not included when submitting.
Any guidance on how to do this would be greatly appreciated.
MainForm
import React from 'react';
import {
useForm,
Controller,
useFieldArray
} from 'react-hook-form';
import {
Button,
TextField,
List,
ListItem,
IconButton,
} from '#material-ui/core';
import DetailForm from '#components/DetailForm'
import AddCircleOutlineIcon from '#material-ui/icons/AddCircleOutline';
function MainForm(props:any) {
const { control, handleSubmit, getValues } = useForm({
mode: 'onBlur',
defaultValues: {
profiles: [
{
firstName: '',
lastName: '',
email: '',
phone: ''
}
]
}
});
const { fields, append, remove } = useFieldArray({
control,
name: 'profiles',
});
const onSubmit = () => {
const data = getValues();
console.log('data: ', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<List>
fields.map((item, index) => {
return (
<ListItem>
<Controller
name={`profiles.${index}.firstName`}
control={control}
render={({field}) =>
<TextField
{ ...field }
label="First Name"
/>
}
/>
<Controller
name={`profiles.${index}.lastName`}
control={control}
render={({field}) =>
<TextField
{ ...field }
label="Last Name"
/>
}
/>
<DetailForm index={index} />
</ListItem>
)
})
</List>
<IconButton onClick={() => append({})}>
<AddCircleOutlineIcon />
</IconButton>
<Button
type='submit'
>
SAVE
</Button>
</form>
)
}
DetailForm
import React from 'react';
import {
Button,
TextField,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
} from '#material-ui/core';
export default function DetailForm(props: any) {
const [dialogState, setDialogState] = React.useState<boolean>(false);
const handleOpen = () => {
setDialogState(true);
};
const handleClose = () => {
setDialogState(false);
};
return (
<>
<Button
onClick={handleOpen}
>
Set Detail Profile
</Button>
<Dialog open={dialogState}>
<DialogTitle>Detail Profile</DialogTitle>
<DialogContent>
<TextField
name={`profiles.${props.index}.email`}
label="Email Address"
/>
<TextField
name={`profiles.${props.index}.phone`}
label="Phone Number"
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>
Cancel
</Button>
<Button onClick={handleClose}>
Add
</Button>
</DialogActions>
</Dialog>
</>
)
}
You have to register those fields inside your <Dialog /> component - just pass control to it. It's also important to set an inline defaultValue when using <Controller /> and useFieldArray. From the docs:
inline defaultValue is required when working with useFieldArray by integrating with the value from fields object.
One other minor thing: You should also pass the complete fieldId to your <Dialog /> component instead of just passing the index. This way it would be easier to change the fieldId in one place instead of editing all fields in your <Dialog /> component.
MainForm
<ListItem key={item.id}>
<Controller
name={`profiles.${index}.firstName`}
control={control}
defaultValue=""
render={({ field }) => (
<TextField {...field} label="First Name" />
)}
/>
<Controller
name={`profiles.${index}.lastName`}
control={control}
defaultValue=""
render={({ field }) => (
<TextField {...field} label="Last Name" />
)}
/>
<DetailForm control={control} fieldId={`profiles.${index}`} />
</ListItem>
DetailForm
export default function DetailForm({ control, fieldId }) {
const [dialogState, setDialogState] = React.useState(false);
const handleOpen = () => {
setDialogState(true);
};
const handleClose = () => {
setDialogState(false);
};
return (
<>
<Button onClick={handleOpen}>Set Detail Profile</Button>
<Dialog open={dialogState}>
<DialogTitle>Detail Profile</DialogTitle>
<DialogContent>
<Controller
name={`${fieldId}.email`}
control={control}
defaultValue=""
render={({ field }) => (
<TextField {...field} label="Email Address" />
)}
/>
<Controller
name={`${fieldId}.phone`}
control={control}
defaultValue=""
render={({ field }) => (
<TextField {...field} label="Phone Number" />
)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleClose}>Add</Button>
</DialogActions>
</Dialog>
</>
);
}
I've created a register form I tried to validate the form onBlur and the one has an email check with backend Apis, I have an issue when I validate any other that case mail check API render every time
I tried with onBlur and onChange validation false that case working fine, But I need onBlur validation, Any Ideas
Thanks for Help
const CompanyRegister = (props) => {
const classes = useStyles();
var [mailError, setMailError] = useState(false);
const initialValues = {
name: "",
website: "",
addressLine1: "",
city: "",
state: "",
mail: "",
};
const validationSchema = yup.object({
name: yup.string().strict().trim().required("Enter the ComapnyName"),
website: yup
.string()
.matches(
/((https?):\/\/)?(www.)?[a-z0-9]+(\.[a-z]{2,}){1,3}(#?\/?[a-zA-Z0-9#]+)*\/?(\?[a-zA-Z0-9-_]+=[a-zA-Z0-9-%]+&?)?$/,
"Enter correct url!"
)
.strict()
.trim()
.required("Enter the Website"),
pincode: yup.number("Invalid pincode").positive("Invalid pincode"),
phone: yup.number("InValid Phone Number"),
altPhone: yup.number("InValid Phone Number"),
mail: yup
.string()
.email("Enter correct Format")
.strict()
.trim()
.required("Enter the Correct Email"),
});
const validCheck = (data) => {
var mailData = { mail: data };
axios.post(baseurl + "companymailcheck", mailData).then((res) => {
if (res.data == "Invalid") {
setMailError(true);
} else {
setMailError(false);
}
});
};
const onSubmit = (data) => {
axios
.post(baseurl + "companyregisteration", data)
.then((res) => {
history.push(`/login`);
toast.success("Successfully Created");
})
.catch((err) => {
toast.error("Some Thing Error");
});
};
return (
<>
<div className={classes.back}>
<Link to="/">Home</Link>
</div>
<Container component="main" className={classes.layout}>
<div className={classes.paper}>
<Typography component="h1" variant="h5">
Register
</Typography>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validateOnChange={false}
validateOnBlur={true}
validationSchema={validationSchema}
>
{({ values, isValid }) => {
return (
<Form autoComplete="off">
<Grid container spacing={4}>
<Grid item xs={12} sm={6}>
<Field
as={TextField}
name="name"
fullWidth
label="Company Name"
autoFocus
required
/>
<ErrorMessage name="name">
{(error) => (
<div className={classes.error}>{error}</div>
)}
</ErrorMessage>
</Grid>
<Grid item xs={12} sm={6}>
<Field
as={TextField}
name="website"
fullWidth
label="Website"
required
/>
<ErrorMessage name="website">
{(error) => (
<div className={classes.error}>{error}</div>
)}
</ErrorMessage>
</Grid>
<Grid item xs={12} sm={12}>
<Field
as={TextField}
name="addressLine1"
type="text"
fullWidth
label="Address Line1"
/>
</Grid>
<Grid item xs={12} sm={6}>
<Field
as={TextField}
name="mail"
fullWidth
label="Mail"
required
validate={() => validCheck(values.mail)}
/>
<ErrorMessage name="mail">
{(error) => (
<div className={classes.error}>{error}</div>
)}
</ErrorMessage>
{mailError ? (
<span style={{ color: "red" }} id="errormessage">
Mail Already There!
</span>
) : null}
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
disabled={!isValid || mailError}
>
Register
</Button>
</Grid>
</Form>
);
}}
</Formik>
</div>
</Container>
</>
);
};
export default CompanyRegister;
As long as it's fine to perform the validation during form submission then I would extend the onSubmit and put the check there.
You can use the second parameter which will return the FormikBag containing all relevant setter's to invalidate the form-field properly. Reference: https://formik.org/docs/api/formik#onsubmit-values-values-formikbag-formikbag--void--promiseany
In this case I would use setFieldError(field, message). Reference: https://formik.org/docs/api/formik#setfielderror-field-string-errormsg-string--void
<Formik initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values, {setFieldError}) => {
return validateMail(values.mail)
.then(() => {
// all good - continue with your submit handler
return onSubmit(values)
})
.catch(error => {
// something wrong? validate the error response + validation message OR set it statically.
setFieldError('mail', 'Mail Already There!')
})
}}
>
I would like to display required, min 1 and max 5 validations in following fields in material-ui dialog. At the moment I have added for one field, but some how validation is not displaying during on click on submit button in dialog. Could someone please advise me on how can i fix the problem.
demo link added here:
https://codesandbox.io/s/tender-wood-3jrwu?file=/src/App.js
const App = () => {
const [rating, setRating] = useState({
ballshooting: "",
dribbling: "",
ballcontrol: "",
sprinting: "",
fitness: ""
});
const [ratingTotal, setRatingTotal] = useState(0);
const [open, setOpen] = React.useState(false);
const { handleSubmit, register, errors } = useForm();
const isMounted = useRef(false);
useEffect(() => {
isMounted.current = true;
return () => (isMounted.current = false);
}, []);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const onChange = e => {
e.persist();
const ratingValues = {
...rating,
[e.target.name]: e.target.value
};
setRating(ratingValues);
ratingCalculation(ratingValues);
};
const ratingCalculation = ratingValues => {
const {
ballshooting,
dribbling,
ballcontrol,
sprinting,
fitness
} = ratingValues;
const newTotal =
Number(ballshooting) +
Number(dribbling) +
Number(ballcontrol) +
Number(sprinting) +
Number(fitness);
const finalAvg = newTotal / 5;
setRatingTotal(finalAvg);
return ratingTotal;
};
return (
<form className="ratingForm">
<Button
className="playerRatingBtn"
onClick={() => handleClickOpen()}
variant="outlined"
color="primary"
>
Enter Player Rating
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="skills-area">
<DialogTitle id="skills-area">Rate the skills of a player</DialogTitle>
<DialogContent>
<label>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="ballshooting"
label="Ball Shooting"
type="text"
fullWidth
min={1}
max={5}
ref={register({
required: "Soccer ball shooting skill is required !"
})}
/>
<span className="newsErrorTextFormat">
{errors.shooting && errors.shooting.message}
</span>
</label>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="dribbling"
label="Dribbling"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="ballcontrol"
label="Ball Control"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="sprinting"
label="Sprinting"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="fitness"
label="Fitness"
type="text"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="secondary">
Cancel
</Button>
<Button type="submit" onClick={() =>
handleClose(
handleSubmit(calculateAvgRating(ratingCalculation(rating)))
)
} color="primary">
Submit
</Button>
</DialogActions>
</Dialog>
</form>
);
};
export default App;
How can I pass ratePlyrEmail to calculateAvgRating() during on click on submit button.
The player email will displays randomly in the <Input type="hidden" name="playertorate" value={email} onChange={onChange}></Input> of DialogContentText every time when the user click on Enter Player Rating button. I have added onChange in the DialogContentText. Now I am getting only rating and loginUserEmail in server side.
const [ratePlyrEmail, setRatePlyrEmail] = useState({ playertorate: ''});
const [rating, setRating] = useState({
shooting: "",
dribbling: "",
ballcontrol: "",
sprinting: "",
fitness: ""
});
const [ratingTotal, setRatingTotal] = useState(0);
const onChange = e => {
e.persist();
const ratingValues = {
...rating,
[e.target.name]: e.target.value
};
setRating(ratingValues);
const rateEmailValue = {
...ratePlyrEmail,
[e.target.name]: e.target.value
}
setRatePlyrEmail(rateEmailValue);
ratingCalculation(ratingValues);
};
const ratingCalculation = ratingValues =>{
const {
shooting,
dribbling,
ballcontrol,
sprinting,
fitness
} = ratingValues;
const newTotal =
Number(shooting) +
Number(dribbling) +
Number(ballcontrol) +
Number(sprinting) +
Number(fitness);
const finalAvg = newTotal / 5;
setRatingTotal(finalAvg);
return ratingTotal ;
}
const calculateAvgRating = (rating) => {
const sendPlayerRating = async () => {
try {
const params = {
email: loginUserEmail,
};
const res = await axios.put('http://localhost:8000/service/playerrating', {ratingTotal:rating}, {params} );
if (res.data.success) {
//rest of code to continue here...
}
else {
console.log(res.data.message);
}
} catch (e) {
console.log(e.response.data.message);
}
}
sendPlayerRating();
};
<div className="container">
<div className="weeklycount_graph_section">
<div>
<Button className="playerRatingBtn" variant="outlined" color="primary" onClick={() => handleClickOpen(setRandPlayerRating())}>
Enter Player Rating
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="skills-area"
>
<DialogTitle id="skills-area">Rate the skills of a player</DialogTitle>
<DialogContent>
{
randPlayers.map(({email, name, photo}) =>(
<DialogContentText key="id">
<Avatar alt="player" src={photo}/>
<Input type="hidden" name="playertorate" value={email} onChange={onChange}></Input>
</DialogContentText>
))
}
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="shooting"
label="Shooting"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="dribbling"
label="Dribbling"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="ballcontrol"
label="Ball Control"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="sprinting"
label="Sprinting"
type="text"
fullWidth
/>
<TextField
autoFocus
onChange={onChange}
margin="dense"
name="fitness"
label="Fitness"
type="text"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="secondary">
Cancel
</Button>
<Button onClick={() => handleClose(calculateAvgRating((ratingCalculation(rating))))} color="primary">
Submit
</Button>
</DialogActions>
</Dialog>
</div>
</div>
</div>
Here i add my sample code.i have a static values getting from state now i want to change this values
render() {
console.log(this.props.selectedVal);
var name,
gender,
city = "";
this.props.data.map((row, index) => {
// console.log(index);
if (this.props.selectedVal === index) {
gender = row[0];
name = row[1];
city = row[2];
}
return [];
});
return (
<Dialog
open={this.props.open}
onClose={this.handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<h1>Edit User</h1>
<DialogContent>
<DialogContentText id="alert-dialog-description" />
<Formik
initialValues={{ name: "", gender: "", city: "" }}
onSubmit={values => console.log(values)}
>
{props => (
<form>
<TextField margin="dense" id="name" label="Name" value={name} />
<br />
<TextField
margin="dense"
id="gender"
label="Gender"
value={gender}
/>
<br />
<TextField margin="dense" label="City" value={city} />
</form>
)}
</Formik>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color="primary">
RESET
</Button>
<Button onClick={this.handleClose} color="primary" autoFocus>
SUBMIT
</Button>
</DialogActions>
</Dialog>
);
}
I am getting the values from the table row.Now i want to change these values using formik. How can I edit these values
Add a onChange listener in your textfield and map the value to a state variable:
textChange(e) {
const value = e.target.value
this.setState({ value })
}
render() {
return (
<TextField value={this.state.value} onChange={this.textChange} .. />
)
}
If you want to handle more values from different textfields using one method try something like this:
textChange(e) {
const id = e.target.id
const value = e.target.value
this.setState({ [id]: value })
}
render() {
return (
<TextField id="gender" value={this.state.gender} onChange={this.textChange} .. />
<TextField id="dense" value={this.state.dense} onChange={this.textChange} .. />
)
}
It seems like you get data with props from parent component. And, if I get it wright, question is how to change state of parent component? You need to add to props function binded to parent`s context and use setState there.