I am trying to create a form to collect input to create an event listing.
I am using the 'value' property in way it is documented in the React docs:
https://reactjs.org/docs/forms.html
,to collect user input onChange, but material-ui:
https://material-ui.com/api/input/
uses 'value' for setting value to the input field not collecting the value in the field. This is causing all sort of bugs, e.i. not being able to enter input, pickers not displaying default values and not accepting values and also not being able to collect information. I could use pointers on where I am going wrong. Thanks for having a look :-)
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Input from '#material-ui/core/Input';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap',
}
});
class CreateEvent extends React.Component{
constructor() {
super();
this.state = {
title: "",
description: "",
location: "",
start_time: "",
start_date: "",
end_time: "",
end_date: ""
};
}
updateInput = e => {
this.setState({
[e.target.name]: e.target.value
});
};
cancelCreation = e =>{
};
addNewEvent = e =>{
//Make db call
};
render(){
return(
<div className="form-container">
<Paper className="new-event-form">
<form className="event-form" onSubmit={this.addNewEvent}>
<Input
type="text"
placeholder="Event title"
className="event-title"
inputProps={{
'aria-label': 'Description',
}}
multiline={1}
rows={1}
onChange={this.updateInput}
value={this.state.value}
/>
<Input
type="text"
placeholder="Event description"
className="event-title"
inputProps={{
'aria-label': 'Description',
}}
multiline={true}
rows={10}
onChange={this.updateInput}
//value={this.state.description}
/*
Will allow user to provide input because 'value' is commented out. But the above component will not because value is referenced
*/
/>
<Grid container spacing={16} className="event-grid">
<Grid item xs={4}>
<Input
type="text"
item xs={4}
placeholder="Event location"
className="event-location"
inputProps={{
'aria-label': 'Description',
}}
multiline={true}
rows={4}
onChange={this.updateInput}
//value={this.state.location}
/>
</Grid>
<Grid item xs={4}>
<TextField
id="date"
label="Start date"
type="date"
defaultValue="2017-05-24"
className="event-start-date"
InputLabelProps={{
shrink: true,
}}
onChange={this.updateInput}
value={this.state.start_date}
/>
<TextField
id="time"
label="Start time"
type="time"
defaultValue="07:30"
className="event-start-time"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
/>
</Grid>
<Grid item xs={4}>
<TextField
id="date"
label="End date"
type="date"
defaultValue="2017-05-24"
className="event-start-date"
InputLabelProps={{
shrink: true,
}}
/>
<TextField
id="time"
label="End time"
type="time"
defaultValue="07:30"
className="event-start-time"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
/>
</Grid>
</Grid>
<Button className="line-spacer"/>
<Grid container className="form-buttons">
<Grid item xs={12}>
<Input type="file" name="my-event-image" id="file" className="new-event-image"> </Input>
<label htmlFor="file">CHOOSE AN IMAGE</label>
<Button className="line-spacer" onChange={this.updateInput}/>
</Grid>
</Grid>
<Grid container spacing={16} className="form-buttons">
<Grid item xs={6}>
<Button onChange={this.cancelCreation}>Cancel</Button>
</Grid>
<Grid item xs={6}>
<Button type="submit">Submit</Button>
</Grid>
</Grid>
</form>
</Paper>
</div>
);
}
}
export default withStyles(styles)(CreateEvent);
In your updateInput method you use e.target.name but none of your input fields have a name property. Add a name property on each Input component matching the name you are using in your value. For example:
<Input name="description" ... value={this.state.description} .../>
Related
I am using MUI to develop my dashboard.
I am using MUI dropdown component to select the vendor details and there are two options for the Vendor dropdown. i.e "Surya Kumar Yadav", "Eswar". I used the same dropdown in all my cards. When I select the one of the dropdown in any card, it is reflected in all the other cards.
import React from "react";
import {
Box,
Card,
CardContent,
Typography,
Button,
CardActionArea,
CardActions,
Grid,
Select,
MenuItem,
FormControl,
InputLabel,
} from "#mui/material";
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { HttpClient } from "../http/http";
import { ConfirmDialog, confirmDialog } from "./ConfirmDialog";
const Services = () => {
const [services, setServices] = useState([]);
const [vendors, setVednors] = useState([]);
const [selectedVendor, setSelectedVendor] = useState("");
const PF =
"https://firebasestorage.googleapis.com/v0/b/mern-stack-service-app.appspot.com/o/";
const navigate = useNavigate();
useEffect(() => {
init();
}, []);
const init = () => fetchServices();
const fetchServices = async () => {
const services = await HttpClient.get(`services`);
const vendors = await HttpClient.get(`vendor`);
setVednors(vendors);
setServices(services);
};
const handleChange = (event) => {
setSelectedVendor(event.target.value);
};
console.log(services);
return services.length === 0 ? (
<Grid>
<Typography
sx={{
fontSize: 32,
color: "blue",
display: "flex",
justifyContent: "center",
alignItems: "center",
textDecoration: "underline",
}}
>
Loading your services.....
</Typography>
</Grid>
) : (
<Grid container>
<ConfirmDialog />
<Grid container item xs={12} columnGap={2}>
<Box sx={{ float: "right" }}>
<Button
variant="outlined"
onClick={() => {
confirmDialog("Are you sure want to logout?", () => {
localStorage.clear();
navigate("/login");
});
}}
>
Logout
</Button>
</Box>
</Grid>
<Grid container item xs={12} rowGap={2}>
{services.map((service, index) => (
<Grid item xs={12} sm={6} md={4} key={index}>
<Card>
<CardActionArea>
<CardContent>
<Grid
component="img"
sx={{
height: 150,
width: 350,
maxHeight: { xs: 150, md: 175 },
maxWidth: { xs: 350, md: 375 },
objectFit: "cover",
}}
alt="The house from the offer."
src={
PF +
service.photo +
"?alt=media&token=c19f2d0a-f254-4391-b589-ef7ee3cad9f5"
}
></Grid>
<Grid item xs={12}>
<Typography textAlign="center">
{service.service}
</Typography>
</Grid>
<Grid item xs={12}>
<FormControl sx={{ minWidth: 120 }} size="small" fullWidth>
<InputLabel id="demo-select-small">
Select Vendor
</InputLabel>
<Select
size="small"
labelId="demo-select-small"
id="demo-select-small"
value={selectedVendor}
label="Select Vendor"
onChange={handleChange}
>
{vendors.map((vendor) => (
<MenuItem
key={vendor._id}
value={vendor.name}
id={vendor.name}
>
{vendor.name}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
</CardContent>
</CardActionArea>
<CardActions>
<Grid item xs={12}>
<Button variant="outlined" color="primary" fullWidth>
Book Service
</Button>
</Grid>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Grid>
);
};
export default Services;
I want the dropdown action reflect in the selected card.
It's because all of your mapped Select elements get their value from selectedVendor. you can change the code like this using these steps:
First:
Change the selectingVendor useState like this:
const [selectedVendors, setSelectedVendors] = useState([]);
Second:
And the handleChange function
const handleChange = (event,index) => {
const currentSelectedVendors = [...selectedVendors];
currentSelectedVendors[index] = event.target.value
setSelectedVendors(currentSelectedVendors);
};
Third:
You need to change the Select Mui component props.
<Select
size="small"
labelId="demo-select-small"
id="demo-select-small"
value={selectedVendors[index] || ""}
label="Select Vendor"
onChange={(e) => handleChange(e,index)}
>
Although there is another solution for this problem and you can pull <Select> logic out of Services component , I mean it's selectingVendor hook and it's change handler and create a new separate component which handles it.
PS: I think it should be better to use vendor._id as the value prop of your MenuItems
I'm building a form where it has two checkboxes the user is allowed to choose only one not both of them
I have used the react-bootstrap form
here is my code:
const [checked, setChecked] = useState("");
const [sechecked, setSechecked] = useState("");
return
<Form style={{ display: "flex" }}>
<Form.Check
value={checked}
onChange={(e) => setChecked(e.target.checked)}
type="radio"
aria-label="radio 1"
/>
Turkiye'den gonder
<Form.Check
value={sechecked}
onChange={(e) => {
setSechecked(e.target.sechecked);
console.log(checked);
}}
type="radio"
aria-label="radio 2"
/>
Turkiye'ye getir
</Form>
so, what I want is to only choose one checkbox and after that, the user can't choose the other one
Kindly add a name property to indicate the two options point to the same thing. In that case, only one can be selected
const [checked, setChecked] = useState("");
const [sechecked, setSechecked] = useState("");
return
<Form style={{ display: "flex" }}>
<Form.Check
value={checked}
name="useroption"
onChange={(e) => setChecked(e.target.checked)}
type="radio"
aria-label="radio 1"
/>
Turkiye'den gonder
<Form.Check
value={sechecked}
name="useroption"
onChange={(e) => {
setSechecked(e.target.sechecked);
console.log(checked);
}}
type="radio"
aria-label="radio 2"
/>
Turkiye'ye getir
</Form>
Someone help me, I built an application with NEXT and Material UI and now for some reason even though the dev server works perfectly fine the productio build is breaking. I tried to alter my _document.ts as shown below:
import Document, { Html, Head, Main, NextScript } from 'next/document';
import React from 'react';
import { ServerStyleSheets } from '#mui/styles';
export default class MyDocument extends Document {
render() {
return (
<Html lang='en'>
<Head>
<link rel='preconnect' href='https://fonts.googleapis.com' />
<link rel='preconnect' href='https://fonts.gstatic.com' />
<link
href='https://fonts.googleapis.com/css2?family=Roboto:wght#100;300;400&display=swap'
rel='stylesheet'
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
and this is _app.tsx:
import '../styles/root.scss';
import '../styles/global/_page.scss';
import { Provider } from 'react-redux';
import type { AppProps } from 'next/app';
import Notification from '../components/Common/Notifications/Notification';
import store from '../redux/app/store';
import React from 'react';
import MIUIHeader from '../components/Common/MUIComponents/MUI-Header/MUIHead';
export default function MyApp({ Component, pageProps }: AppProps) {
React.useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
//#ts-ignore
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<Provider store={store}>
<React.Fragment>
<MIUIHeader />
<Component {...pageProps} />
<Notification />
</React.Fragment>
</Provider>
);
}
And here's my Material UI Component:
import React, { ReactElement } from 'react';
import TextField from '#mui/material/TextField';
import LocalPhoneIcon from '#mui/icons-material/LocalPhone';
import MuiPhoneNumber from 'material-ui-phone-number';
import Autocomplete from '#mui/material/Autocomplete';
import Button from '#mui/material/Button';
import MailIcon from '#mui/icons-material/Mail';
import PersonIcon from '#mui/icons-material/Person';
import LocationOnIcon from '#mui/icons-material/LocationOn';
import LocationCityIcon from '#mui/icons-material/LocationCity';
import BusinessCenterIcon from '#mui/icons-material/BusinessCenter';
import ShareIcon from '#mui/icons-material/Share';
import classes from './VibeForms.module.scss';
import CorporateFareIcon from '#mui/icons-material/CorporateFare';
import ImageCarasoul from '../../../ImageCarasoul/ImageCarasoul';
interface Props {}
const imagePaths = [
'/screen-graphics/signup/individual/graphic-1.svg',
'/screen-graphics/signup/individual/graphic-2.svg',
'/screen-graphics/signup/individual/graphic-3.svg',
];
const maxMultilineRows = 3;
export default function LoginForm({}: Props): ReactElement {
return (
<React.Fragment>
<div className={classes.LoginColumnGrid}>
<ImageCarasoul imagePaths={imagePaths} />
<div className={classes.LoginColumn}>
<div className={classes.LoginRowGrid}>
<PersonIcon />
<TextField
required
className={classes.FormInputField}
label='Full Name'
size='small'
type='text'
/>
</div>
<div className={classes.LoginRowGrid}>
<MailIcon />
<TextField
required
className={classes.FormInputField}
label='Email Address'
size='small'
type='email'
/>
</div>
<div className={classes.LoginRowGrid}>
<LocalPhoneIcon />
<MuiPhoneNumber
variant='outlined'
name='phone'
data-cy='user-phone'
required
className={classes.FormInputField}
label='Phone Number'
size='small'
defaultCountry={'in'}
onChange={(e) => console.log(e)}
/>
</div>
<div className={classes.LoginRowGrid}>
<ShareIcon />
<TextField
required
className={classes.FormInputField}
label='Social Media Links'
size='small'
type='text'
multiline
maxRows={maxMultilineRows}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<Autocomplete
disablePortal
options={[
'Workshop/Training',
'Social Media Promotion',
'Our Tie Ups',
'Networking',
]}
renderInput={(params) => (
<TextField
{...params}
label='Services you are looking for'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<TextField
required
className={classes.FormInputField}
label='Any Other Specific Requests?'
size='small'
type='text'
multiline
maxRows={maxMultilineRows}
/>
</div>
</div>
<div className={classes.LoginColumn}>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<Autocomplete
disablePortal
options={['Beginer', 'Intermediate', 'Expert']}
renderInput={(params) => (
<TextField
{...params}
label='Enter your Profession'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<LocationOnIcon />
<Autocomplete
disablePortal
options={['Bangalore', 'Chennai', 'Hyderabad', 'Mumbai']}
renderInput={(params) => (
<TextField
{...params}
label='State'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<LocationCityIcon />
<Autocomplete
disablePortal
options={['Bangalore', 'Chennai', 'Hyderabad']}
renderInput={(params) => (
<TextField
{...params}
label='City'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<Autocomplete
disablePortal
options={['Beginer', 'Intermediate', 'Expert']}
renderInput={(params) => (
<TextField
{...params}
label='Profession Level'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<BusinessCenterIcon />
<TextField
required
className={classes.FormInputField}
label='Work Experience'
size='small'
type={'number'}
InputProps={{
endAdornment: <label>Years</label>,
}}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<TextField
required
className={classes.FormInputField}
label='Your Achievements'
size='small'
type='text'
multiline
maxRows={maxMultilineRows}
/>
</div>
</div>
</div>
<Button
variant='contained'
style={{ alignSelf: 'end', margin: '1rem', width: '10rem' }}>
Next
</Button>
</React.Fragment>
);
}
I am using sass modules along with Material UI in NEXT. I tried all the solutions out there mentioned in github and stack overflow but nothing seems to work ;(.
Since most of the documents were outdated to an older version of Material UI, I found the solution in their github repo. They have updated it 20 days back and it works fine. In case someone may want to reference this in future, kindly keep checking this repo.
Here you will find the exact configuration you need to do for _app.tsx and _document.tsx. Additionally you need to also create two files for themes and createEmotionCache as well. The location doesn't matter though.
I am trying to submit a form using react hook forms. After submit i want to clear all the fields. I have read about using reset(). But its not working
import React, { Fragment } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
import * as Yup from "yup";
import "react-toastify/dist/ReactToastify.css";
import {
Paper,
Box,
Grid,
TextField,
Typography,
Button,
} from "#material-ui/core";
export default function ResetPassword() {
const validationSchema = Yup.object().shape({
old_password: Yup.string().required("Password is required"),
new_password1: Yup.string().required("Password is required"),
new_password2: Yup.string().required("Password is required"),
});
const { register, handleSubmit, reset } = useForm({
resolver: yupResolver(validationSchema),
});
const onSubmit = (data) => {
console.log(data);
reset();
};
return (
<Fragment>
<Paper variant="outlined">
<Box px={3} py={2}>
<Typography variant="h6" align="center" margin="dense">
Change Password
</Typography>
<Grid container spacing={1}>
<Grid item xs={12} sm={12}>
<TextField
required
label="Current Password"
type="password"
{...register("old_password")}
/>
</Grid>
<Grid item xs={12} sm={12}>
<TextField
required
label="New Password"
type="password"
{...register("new_password1")}
/>
</Grid>
<Grid item xs={12} sm={12}>
<TextField
required
label="Confirm New Password"
type="password"
{...register("new_password2")}
/>
</Grid>
</Grid>
<Box mt={3}>
<Button
variant="contained"
color="primary"
onClick={handleSubmit(onSubmit)}
>
Change Password
</Button>
</Box>
</Box>
</Paper>
</Fragment>
);
}
How to reset the fields after submit
You have to use RHF's <Controller /> component here as register won't work with MUI's <Textfield /> because it is an external controlled component. You can find here more information about integrating UI libraries.
One important thing is to pass defaultValues to useForm, as this is required when using reset for external controlled components (Docs).
You will need to pass defaultValues to useForm in order to reset the
Controller components' value.
export default function ResetPassword() {
const validationSchema = Yup.object().shape({
old_password: Yup.string().required("Password is required"),
new_password1: Yup.string().required("Password is required"),
new_password2: Yup.string().required("Password is required")
});
const { control, handleSubmit, reset } = useForm({
resolver: yupResolver(validationSchema),
defaultValues: {
old_password: "",
new_password1: "",
new_password2: ""
}
});
const onSubmit = (data) => {
console.log(data);
reset();
};
return (
<Fragment>
<Paper variant="outlined">
<Box px={3} py={2}>
<Typography variant="h6" align="center" margin="dense">
Change Password
</Typography>
<Grid container spacing={1}>
<Grid item xs={12} sm={12}>
<Controller
name="old_password"
control={control}
render={({ field: { ref, ...field } }) => (
<TextField
{...field}
inputRef={ref}
fullWidth
required
label="Current Password"
type="password"
/>
)}
/>
</Grid>
<Grid item xs={12} sm={12}>
<Controller
name="new_password1"
control={control}
render={({ field: { ref, ...field } }) => (
<TextField
{...field}
inputRef={ref}
fullWidth
required
label="New Password"
type="password"
/>
)}
/>
</Grid>
<Grid item xs={12} sm={12}>
<Controller
name="new_password2"
control={control}
render={({ field: { ref, ...field } }) => (
<TextField
{...field}
inputRef={ref}
fullWidth
required
label="Confirm New Password"
type="password"
/>
)}
/>
</Grid>
</Grid>
<Box mt={3}>
<Button
variant="contained"
color="primary"
onClick={handleSubmit(onSubmit)}
>
Change Password
</Button>
</Box>
</Box>
</Paper>
</Fragment>
);
}
I have a dialog with material ui where i have a text and then 4 fields textfield.
The problem is that the elements inside dialogcontent doesnt fill all the space in the dialog leaving one blank space on the right side.
I would like to give some space between the textfields in the same row as well as space-evenly these elements.
This is my dialog definition:
import React from "react";
import Dialog from "#material-ui/core/Dialog";
import DialogTitle from "#material-ui/core/DialogTitle";
import {
DialogActions,
Button,
DialogContent,
DialogContentText,
} from "#material-ui/core";
import { faExclamationTriangle } from "#fortawesome/free-solid-svg-icons";
import { addProduct } from "../view/Items/Items";
import { useState } from "react";
import { TextField } from "#material-ui/core";
import { updateValues } from "../view/Items/Items";
import { Grid } from "#material-ui/core";
export default function AlertPopOverParametros({
producto,
año,
open,
setOpen,
onAccept,
}) {
const [coef_transp, setCoefTransp] = useState();
const [coef_comer, setCoefComer] = useState();
const [km, setKm] = useState();
const [prod, setProd] = useState();
return (
<Dialog fullWidth aria-labelledby="simple-dialog-title" open={open}>
<DialogTitle id="simple-dialog-title">
<b>Configuración del producto</b>
</DialogTitle>
<DialogContent>
<DialogContentText>
Introduzca los valores necesarios para el cálculo del valor de huella
hídrica
</DialogContentText>
<TextField
onChange={(e) => setCoefTransp(e.target.value)}
autoFocus
label="Coeficiente de Transporte"
type="number"
value={coef_transp}
/>
<TextField
onChange={(e) => setCoefComer(e.target.value)}
autoFocus
label="Coeficiente de Comercialización"
type="number"
value={coef_comer}
/>
<TextField
onChange={(e) => setKm(e.target.value)}
autoFocus
label="Kilómetros"
type="number"
value={km}
/>
<TextField
onChange={(e) => setProd(e.target.value)}
autoFocus
label="Producción"
type="number"
value={prod}
/>
</DialogContent>
<DialogActions>
{onAccept ? (
<Button
style={{
backgroundColor: "#0068ff",
color: "white",
borderRadius: "5px",
}}
fullWidth
onClick={() => {
onAccept();
console.log(producto, año, coef_transp, coef_comer, km, prod);
updateValues(producto, año, coef_transp, coef_comer, km, prod);
setOpen(false);
}}
>
Aceptar
</Button>
) : null}
</DialogActions>
</Dialog>
);
}
I have tried to wrap everything in a Grid element and specifying the size of it but it didn't work.
Thank you so much.
Using a Grid and fullWidth property on the Textfields will solve your issue:
<Grid container>
<Grid xs={6}>
<TextField
fullWidth
...
/>
</Grid>
<Grid xs={6}>
<TextField
fullWidth
...
/>
</Grid>
<Grid xs={6}>
<TextField
fullWidth
...
/>
</Grid>
<Grid xs={6}>
<TextField
fullWidth
...
/>
</Grid>
</Grid>
The Grid will order the textfields into two rows and two columns by setting xs to 6 on the grid childs. Then in order to make your textfields span the entire width of the grid child you can use the fullWidth property.