Javascript 表单验证和清除
我是一个新的前端开发人员。仍在努力找出基本点。我正在编写一个应用程序,它要求用户在弹出对话框的表单中输入信息。当框关闭时,我希望表单完全重置/清除,如果数据有效,我希望将其添加到状态并显示在表中 我已经完成了表单数据输入部分的工作,但我目前正在努力清除表单并实现验证。特别是当窗体关闭时(取消按钮),它需要清除并重置,直到对话框再次打开。当按下提交按钮时,我需要运行某种形式的表单验证,以检查每个值在添加到状态之前是否输入正确。我没有比我更高级别的人来审查我的代码并提供提示/帮助,所以我希望有一个高级别的人能给我指出正确的方向,让我知道我在代码中做错了什么 欢迎根据我的任何代码提供任何其他有用的提示。。我知道这可能有点奇怪和混乱。。我在学习和构建示例,所以请原谅我糟糕/混乱的代码 主app.js代码:Javascript 表单验证和清除,javascript,reactjs,forms,validation,Javascript,Reactjs,Forms,Validation,我是一个新的前端开发人员。仍在努力找出基本点。我正在编写一个应用程序,它要求用户在弹出对话框的表单中输入信息。当框关闭时,我希望表单完全重置/清除,如果数据有效,我希望将其添加到状态并显示在表中 我已经完成了表单数据输入部分的工作,但我目前正在努力清除表单并实现验证。特别是当窗体关闭时(取消按钮),它需要清除并重置,直到对话框再次打开。当按下提交按钮时,我需要运行某种形式的表单验证,以检查每个值在添加到状态之前是否输入正确。我没有比我更高级别的人来审查我的代码并提供提示/帮助,所以我希望有一个高
import React, { Component } from 'react';
import './App.css';
import Form from './Form'
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Button from '@material-ui/core/Button';
import Snackbar from '@material-ui/core/Snackbar';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/styles';
const styles = theme => ({
root: {
container: {
background: 'yellow'
},
paper: {
background: 'red'
},
div:{
background:'black'
},
table: {
width: '100%'
},
}
});
class App extends Component {
constructor() {
super();
this.state = {
dialogOpen: false,
formClear: true,
snackBarOpen: false,
snackBarMessage: 'default meesage',
tableData: {},
formData: {
projectName: '',
jobNumber: '',
accountField: {
selected: [],
options:[
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'},
{value: 'test data', label: 'test data'}
]
},
projectDescription: '',
designOrDigital: {
options:[
{label: 'option A', value: 'optionA'},
{label: 'option B', value: 'optionB'}
],
value: {}
},
mediaDeliverables: [
{label: 'different', checked: false, quantity:'', details:''},
{label: 'types', checked: false, quantity:'', details:''},
{label: 'of', checked: false, quantity:'', details:''},
{label: 'media', checked: false, quantity:'', details:''},
{label: 'deliverables', checked: false, quantity:'', details:''},
],
timing: {
entries : [
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
{entry: 'Dummy Values', planned:null, actual:null, notes:''},
]
},
projectMap: {
selected: null
}
}
}
}
// -----------------
// SETTERS
// -----------------
setAccountsSelected = (selected) => {
let currentState = this.getStateCopy();
currentState.formData.accountField.selected = selected;
this.setState(currentState);
}
setProjectName = (value) => {
let currentState = this.getStateCopy();
currentState.formData.projectName = value;
this.setState(currentState);
}
setJobNumber = (value) => {
let currentState = this.getStateCopy();
currentState.formData.jobNumber = value;
this.setState(currentState);
}
setProjectDescription = (value) => {
let currentState = this.getStateCopy();
currentState.formData.projectDescription = value;
this.setState(currentState);
}
setDesignOrDigital = (value) => {
let currentState = this.getStateCopy();
currentState.formData.designOrDigital.value = value;
this.setState(currentState);
}
setMediaDeliverable = (value, index) => {
let currentState = this.getStateCopy();
currentState.formData.mediaDeliverables[index] = value;
this.setState(currentState);
}
setTimingData = data => {
let currentState = this.getStateCopy();
currentState.formData.timing.entries = data;
this.setState(currentState);
}
setSelectedProjectMapId = data => {
let currentState = this.getStateCopy();
currentState.formData.projectMap.selected = data;
this.setState(currentState);
}
setFormClear = (value) => {
let currentState = this.getStateCopy();
currentState.formClear = value;
this.setState(currentState);
}
// -----------------
// HANDLERS
// -----------------
onDialogOpen = () => this.setState({dialogOpen:true});
onDialogClose = () => this.setState({dialogOpen:false});
onSnackbarClose = (e, reason) => {
if (reason === 'clickaway') {
return;
}
this.setState({snackBarOpen:false});
this.setState({snackBarMessage:''});
};
onCreate = () => {
let theSnackBarMessage = `${this.state.formData.projectName} created`;
this.setState({snackBarOpen:true});
this.setState({snackBarMessage:theSnackBarMessage});
this.onDialogClose();
};
// I was told to use Object.assign to avoid direct mutation of state..
getStateCopy = () => Object.assign({}, this.state);
render() {
const { classes } = this.props;
return (
<div className="App">
<br /><br /><br />
<Button color="primary" onClick={this.onDialogOpen}>
Create New Resourcing Request
</Button>
<br /><br /><br />
<div>Table of information will go here</div>
<Dialog
className={classes.root}
open={this.state.dialogOpen}
onClose={this.onDialogClose}
maxWidth = {'md'}
>
<DialogTitle>Resource Request Form</DialogTitle>
<DialogContent>
<Form
projectName={this.state.formData.projectName}
jobNumber={this.state.formData.jobNumber}
accountSelected={this.state.formData.accountField.selected}
projectDescription={this.state.formData.projectDescription}
selectedProjectMapId={this.state.formData.projectMap.selected}
accountFieldOptions={this.state.formData.accountField.options}
designOrDigital={this.state.formData.designOrDigital}
mediaDeliverablesOptions={this.state.formData.mediaDeliverables}
timingData={this.state.formData.timing.entries}
setAccountsSelected={this.setAccountsSelected}
setProjectName={this.setProjectName}
setDesignOrDigital={this.setDesignOrDigital}
setMediaDeliverable={this.setMediaDeliverable}
setJobNumber={this.setJobNumber}
setProjectDescription={this.setProjectDescription}
setTimingData={this.setTimingData}
setSelectedProjectMapId={this.setSelectedProjectMapId}
setFormClear={this.setFormClear}
dialogOpen = {this.state.dialogOpen}
formClear = {this.state.formClear}
/>
</DialogContent>
<DialogActions>
<Button onClick={this.onDialogClose} color="primary">
Cancel
</Button>
<Button
variant="contained"
onClick={this.onCreate}
color="primary"
>
Create
</Button>
</DialogActions>
</Dialog>
<Snackbar
open={this.state.snackBarOpen}
message={this.state.snackBarMessage}
onClose={this.onSnackbarClose}
autoHideDuration={4000}
/>
</div>
);
}
}
App.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(App);
//export default App;
import React,{Component}来自'React';
导入“/App.css”;
从“./Form”导入表单
从“@material ui/core/Dialog”导入对话框;
从“@material ui/core/DialogActions”导入DialogActions;
从“@material ui/core/DialogContent”导入DialogContent;
从“@material ui/core/DialogContentText”导入DialogContentText;
从“@material ui/core/DialogTitle”导入DialogTitle;
从“@material ui/core/Button”导入按钮;
从“@material ui/core/Snackbar”导入Snackbar;
从“道具类型”导入道具类型;
从'@material ui/styles'导入{withStyles};
常量样式=主题=>({
根目录:{
容器:{
背景:“黄色”
},
论文:{
背景:“红色”
},
分区:{
背景:“黑色”
},
表:{
宽度:“100%”
},
}
});
类应用程序扩展组件{
构造函数(){
超级();
此.state={
dialogOpen:false,
formClear:没错,
snackBarOpen:错,
snackBarMessage:“默认meesage”,
tableData:{},
表格数据:{
项目名称:“”,
作业编号:“”,
会计字段:{
选定:[],
选项:[
{值:'测试数据',标签:'测试数据'},
{值:'测试数据',标签:'测试数据'},
{值:'测试数据',标签:'测试数据'},
{值:'测试数据',标签:'测试数据'},
{值:'测试数据',标签:'测试数据'},
{值:'测试数据',标签:'测试数据'},
{值:'测试数据',标签:'测试数据'},
{值:'测试数据',标签:'测试数据'},
{值:'测试数据',标签:'测试数据'},
{值:'测试数据',标签:'测试数据'},
{值:“测试数据”,标签:“测试数据”}
]
},
项目描述:“”,
设计数字:{
选项:[
{标签:'选项A',值:'选项A'},
{标签:'option B',值:'optionB'}
],
值:{}
},
媒体交付物:[
{标签:'不同',选中:false,数量:'',详细信息:'},
{标签:'types',选中:false,数量:'',详细信息:'},
{标签:'of',选中:false,数量:'',详细信息:'},
{标签:'media',选中:false,数量:'',详细信息:'},
{标签:'可交付成果',选中:false,数量:'',详细信息:'},
],
时间:{
参赛作品:[
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
{条目:'伪值',计划值:null,实际值:null,注释:'},
]
},
项目地图:{
所选:空
}
}
}
}
// -----------------
//二传手
// -----------------
setAccountsSelected=(selected)=>{
让currentState=this.getStateCopy();
currentState.formData.accountField.selected=已选择;
此.setState(当前状态);
}
setProjectName=(值)=>{
让currentState=this.getStateCopy();
currentState.formData.projectName=值;
此.setState(当前状态);
}
setJobNumber=(值)=>{
让currentState=this.getStateCopy();
currentState.formData.jobNumber=值;
此.setState(当前状态);
}
setProjectDescription=(值)=>{
让currentState=this.getStateCopy();
currentState.formData.projectDescription=值;
此.setState(当前状态);
}
setDesignOrDigital=(值)=>{
让currentState=this.getStateCopy();
currentState.formData.designOrDigital.value=值;
此.setState(当前状态);
}
setMediaDeliverable=(值、索引)=>{
让currentState=this.getStateCopy();
currentState.formData.MediaDeliveries[索引]=值;
此.setState(当前状态);
}
setTimingData=数据=>{
让currentState=this.getStateCopy();
currentState.formData.timing.entries=数据;
此.setState(当前状态);
}
setSelectedProjectMapId=数据=>{
让currentState=this.getStateCopy();
currentState.formData。
import React, { useState, useEffect } from 'react';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import Fab from '@material-ui/core/Fab';
import SendIcon from '@material-ui/icons/Send';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormHelperText from '@material-ui/core/FormHelperText';
import TextField from '@material-ui/core/TextField';
import Select from '@material-ui/core/Select';
import Chip from '@material-ui/core/Chip';
import Checkbox from '@material-ui/core/Checkbox';
import Radio from '@material-ui/core/Radio';
import MediaDeliverablesCheckBox from './MediaDeliverablesCheckBox';
import { default as MaterialRadioGroup } from '@material-ui/core/RadioGroup';
import TimingTable from './TimingTable';
import ProjectMap from './ProjectMap';
const useStyles = makeStyles(theme => ({
container: {
display: 'inline-block',
flexWrap: 'wrap',
width: 1000,
},
extendedIcon: {
marginRight: theme.spacing(1),
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300,
},
textField: {
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
width: 370,
},
dense: {
marginTop: 19,
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: 2,
},
noLabel: {
marginTop: theme.spacing(3),
},
table: {
width: '20%',
}
}));
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
let accountNames = [];
function getStyles(name, accountName, theme) {
return {
fontWeight:
accountName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
const MediaDeliverableCheckBoxList = ({values, label, onMediaDeliverableChange}) => (
<FormControl>
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{values.map((value, index) => (
<MediaDeliverablesCheckBox
key={index}
index={index}
mediaDeliverablesOptions={value}
onMediaDeliverableChange={onMediaDeliverableChange(index)}
/>
))}
</FormGroup>
</FormControl>
);
const CheckboxGroup = ({ values, label, onChange }) => (
<FormControl component="fieldset">
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{values.map((value, index) => (
<FormControlLabel
key={index}
control={
<Checkbox
checked={value.checked}
onChange={onChange(index)}
/>
}
label={value.label}
/>
))}
</FormGroup>
</FormControl>
);
const RadioGroup = ({ value, options, name, label, onChange }) => (
<FormControl component="fieldset">
<FormLabel component="legend">{label}
</FormLabel>
<MaterialRadioGroup
name={name}
value={value}
onChange={onChange}
disabled
>
{options.map((option, index) => (
<FormControlLabel
key={index}
control={<Radio />}
value={option.value}
label={option.label}
/>
))}
</MaterialRadioGroup>
</FormControl>
);
export default function Form(props) {
const classes = useStyles();
const theme = useTheme();
const designOrDigital = props.designOrDigital;
const mediaDeliverablesOptions = props.mediaDeliverablesOptions;
const accountName = props.accountSelected;
const projectName = props.projectName;
accountNames = [...props.accountFieldOptions];
useEffect(() => {
return () => {
//I started experimenting with lifecycle hooks because I thought
//I might need to clear the form at a certain point in the form
//lifcycle, but I'm not 100% sure where I need to do this..
console.log('>> [Form.js] useEffect() = will unmount');
console.log('>> [Form.js] will unmount: checking if form is closed and clear: dialogOpen=',props.dialogOpen,'formClear=',props.formClear);
}
}, []);
useEffect(() => {
console.log('>> [Form.js] useEffect() = mounted or updated');
console.log('>> [Form.js] mounted/updated: checking if form is closed and clear: dialogOpen=',props.dialogOpen,'formClear=',props.formClear);
});
const handleAccountChange = (event) => {
props.setFormClear(false);
props.setAccountsSelected(event.target.value);
}
const handleProjectNameChange = (event) => {
props.setFormClear(false);
props.setProjectName(event.target.value);
};
const handleJobNumberChange = (event) => {
props.setFormClear(false);
props.setJobNumber(event.target.value);
};
const handleProjectDescriptionChange = (event) => {
props.setFormClear(false);
props.setProjectDescription(event.target.value);
};
const onDesignOrDigitalChange = index => ({ target: { value } }) => {
props.setFormClear(false);
props.setDesignOrDigital(value);
};
const onMediaDeliverableChange = index => (deliverableData, e) => {
props.setFormClear(false);
props.setMediaDeliverable(deliverableData, index);
};
const timingChangeHandler = data => {
props.setFormClear(false);
props.setTimingData(data);
};
const clearForm = () => {
console.log('>> form being cleared');
//I couldn't figure out how to actually clear the form
//I experimented with having a boolean value in state to
//flag that form should be cleared.
//I attempted to manually clear the form field values via
//state but it doesn't appear to be working.
props.setFormClear(true);
props.setAccountsSelected([]); //pass empty array to clear current array
props.setProjectName(''); //pass empty string to clear current field.
}
console.log('>> [Form.js] (Form) checking if form is closed and clear: ',props.dialogOpen, props.formClear)
if(props.dialogOpen === false && props.formClear === false) {
//I was trying to watch the form state here to tell teh Form component
//when it needs to clear.. it didn't work so I started experimenting with
//lifecycle hooks above..
console.log('>> [Form.js] (Form) Form is Closed');
clearForm();
}
return (
<div>
<TextField
id="project-name"
label="Project Name"
className={classes.textField}
value={props.projectName}
onChange={handleProjectNameChange}
/>
<br /><br />
<FormControl className={classes.formControl}>
<InputLabel htmlFor="select-multiple-accounts">Account</InputLabel>
<Select
multiple
value={accountName}
onChange={handleAccountChange}
input={<Input id="select-multiple-accounts" />}
renderValue={
selected => (
<div className={classes.chips}>
{
selected.map(value => (
<Chip key={value} label={value} className={classes.chip} />
))}
</div>
)}
MenuProps={MenuProps}
>
{accountNames.map(name => (
<MenuItem key={name.label} value={name.label} style={getStyles(name.label, accountName, theme)}>
{name.label}
</MenuItem>
))}
</Select>
</FormControl>
<br /><br />
<TextField
id="job-number"
label="Job #"
className={classes.textField}
value={props.jobNumber}
onChange={handleJobNumberChange}
fullWidth
/>
<br /><br /><br />
<TextField
id="project-description"
label="Project Description"
placeholder="Provide a detailed description of the project:"
className={classes.textField}
multiline
variant="outlined"
value={props.projectDescription}
onChange={handleProjectDescriptionChange}
fullWidth
/>
<br /><br /><br />
<RadioGroup
label="Please Choose One:"
options={designOrDigital.options}
value={designOrDigital.value.value}
onChange={onDesignOrDigitalChange}
name="designOrDigitalRadio"
/>
<br /><br /><br />
<MediaDeliverableCheckBoxList
label="Please choose deliverables:"
onMediaDeliverableChange={onMediaDeliverableChange}
values={mediaDeliverablesOptions}
/>
<br /><br /><br />
<TimingTable
timingData={props.timingData}
timingChangeHandler={timingChangeHandler}
/>
<ProjectMap
setSelectedProjectMapId={props.setSelectedProjectMapId}
selectedProjectMapId={props.selectedProjectMapId}
/>
</div>
);
}