Javascript 表单验证和清除

Javascript 表单验证和清除,javascript,reactjs,forms,validation,Javascript,Reactjs,Forms,Validation,我是一个新的前端开发人员。仍在努力找出基本点。我正在编写一个应用程序,它要求用户在弹出对话框的表单中输入信息。当框关闭时,我希望表单完全重置/清除,如果数据有效,我希望将其添加到状态并显示在表中 我已经完成了表单数据输入部分的工作,但我目前正在努力清除表单并实现验证。特别是当窗体关闭时(取消按钮),它需要清除并重置,直到对话框再次打开。当按下提交按钮时,我需要运行某种形式的表单验证,以检查每个值在添加到状态之前是否输入正确。我没有比我更高级别的人来审查我的代码并提供提示/帮助,所以我希望有一个高

我是一个新的前端开发人员。仍在努力找出基本点。我正在编写一个应用程序,它要求用户在弹出对话框的表单中输入信息。当框关闭时,我希望表单完全重置/清除,如果数据有效,我希望将其添加到状态并显示在表中

我已经完成了表单数据输入部分的工作,但我目前正在努力清除表单并实现验证。特别是当窗体关闭时(取消按钮),它需要清除并重置,直到对话框再次打开。当按下提交按钮时,我需要运行某种形式的表单验证,以检查每个值在添加到状态之前是否输入正确。我没有比我更高级别的人来审查我的代码并提供提示/帮助,所以我希望有一个高级别的人能给我指出正确的方向,让我知道我在代码中做错了什么

欢迎根据我的任何代码提供任何其他有用的提示。。我知道这可能有点奇怪和混乱。。我在学习和构建示例,所以请原谅我糟糕/混乱的代码

主app.js代码:

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>
  );
}