Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to apply Form validation for React Material-UI TextField and Select?

I'm trying to add validation to TextField and Select before going to handleNext(). Here is the code (2 components):

class Quote extends React.Component {
state = {
    activeStep: 0,
    zipCode: '',
    destination: '',
    sedanPrice: '',
    suvPrice: '',
    labelWidth: 0,
};

getStepContent = (step) => {
    switch (step) {
        case 0:
            return (
                <div>
                    <QuoteForm
                        {...this.state}
                        {...this.state.value}
                        handleChange={this.handleChange}
                    />
                </div>
            )
        case 1:
            return (
                <div>
                    <QuotePrice 
                        {...this.state}
                        {...this.state.value}
                    />
                </div>
            )
        default:
            throw new Error('Unknown step');
    }
}

handleNext = () => {
    this.setState(prevState => ({
        activeStep: prevState.activeStep + 1,
    }));
    switch(this.state.activeStep){
        case 0: 
            // Creates an API call for sedan pricing
            API.getPrice(this.state.zipCode, "sedan" + this.state.destination).then(res => {
                let price = res.data;
                let key = Object.keys(price);
                console.log("Sedan price is $" + price[key]);
                this.setState({sedanPrice: price[key]});
            })
            .catch(err => console.log(err));

            // Creates an API call for SUV pricing
            API.getPrice(this.state.zipCode, "suv" + this.state.destination).then(res => {
                let price = res.data;
                let key = Object.keys(price);
                console.log("SUV price is $" + price[key])
                this.setState({suvPrice: price[key]});
            })
            .catch(err => console.log(err));
        break
        case 1:
            console.log('forward to booking page');
            window.location.href = '/booking';
        break
        default: 
            console.log('over');
    }
};

handleBack = () => {
    this.setState(state => ({
        activeStep: state.activeStep - 1,
        sedanPrice: '',
        suvPrice: '',
        destination: '',
        zipCode: '',
    }));
};

handleReset = () => {
    this.setState({
        activeStep: 0,
    });
};

handleChange = event => {
    const { name, value } = event.target;
    this.setState({
        [name]: value,
    });
};

render() {
    const { classes } = this.props;
    const { activeStep } = this.state;
    const steps = ['Pick Up Address', 'Select Your Vehicle'];

    return (
        <React.Fragment>
            <CssBaseline />
            <main className={classes.layout}>
                <Paper className={classes.paper}>
                    <React.Fragment>
                        {activeStep === steps.length ? (
                            <React.Fragment>
                                <Typography variant="h5" gutterBottom>
                                    Thank you for your interest!
                                </Typography>
                            </React.Fragment>
                            ) : (
                            <React.Fragment>
                                {this.getStepContent(activeStep)}
                                <div className={classes.buttons}>
                                    {activeStep !== 0 && (
                                        <Button onClick={this.handleBack} className={classes.button}>
                                            Back
                                        </Button>
                                    )}
                                    <Button
                                        variant="contained"
                                        color="primary"
                                        onClick={this.handleNext}
                                        className={classes.button}
                                    >
                                        {activeStep === steps.length - 1 ? 'Book Now' : 'Next'}
                                    </Button>
                                </div>
                            </React.Fragment>
                            )}
                    </React.Fragment>
                </Paper>
            </main>
        </React.Fragment>
    );
}

}

QuoteForm.js

export class QuoteForm extends React.Component {

state = {
    zipCode: this.props.zipCode,
    destination: this.props.destination,
}

render() {

    const { classes } = this.props;

    return (
        <React.Fragment>
            <Typography variant="h5" align="center">
                Enter your zip code for a quick quote
            </Typography>
            <Grid container>
                <Grid className={classes.TextField} item xs={12} sm={6}>
                    <TextField
                        required
                        id="zip"
                        name="zipCode"
                        label="Zip / Postal code"
                        fullWidth
                        autoComplete="billing postal-code"
                        value={this.props.zipCode}
                        onChange={this.props.handleChange}
                    />
                </Grid>
                <FormControl xs={12} sm={6} className={classes.formControl}>
                    <Select
                        required
                        value={this.props.destination}
                        onChange={this.props.handleChange}
                        input={<Input name="destination" />}
                        displayEmpty
                        name="destination"
                        className={classes.selectEmpty}
                    >
                        <MenuItem value="">
                            <em>Select Your Airport *</em>
                        </MenuItem>
                        <MenuItem name="SAN" value={"SAN"}>San Diego International Airport</MenuItem>
                        <MenuItem name="LAX" value={"LAX"}>Los Angeles International Airport</MenuItem>
                    </Select>
                </FormControl>
            </Grid>
        </React.Fragment>
    );
}

}

I tried 2 different ways. First, using Button disabled and writing a function to handle the validation and set disabled to false. Second, using npm package to handle validation. Both failed since I'm new to this. Any help would be appreciated. Thanks in advance.

like image 658
Bojan Mitrovic Avatar asked May 23 '20 01:05

Bojan Mitrovic


1 Answers

Do the following:

  • maintain a boolean state say error and errorMessage
  • in handleNext, validate he input values and set error to false and set a message to the error.
  • For material ui Textfield, use error and helperText props to set/display errors nicely beside your fields
  • For material ui Select, use FormControl error prop and provide label to Select in order to set/display errors nicely beside your fields
  • don't let the user go to next form until error is fixed.
  • pass error and errorMessage to QuoteForm component.

Working copy of your code is here in the codesandbox

state

state = {
    activeStep: 0,
    zipCode: "",
    destination: "",
    sedanPrice: "",
    suvPrice: "",
    labelWidth: 0,
    error: false, //<---- here
    errorMessage: {} //<-----here
  };

handleNext

handleNext = () => {
    let isError = false;
    if (this.state.zipCode.length < 2) {
      isError = true;
      this.setState({
        error: true,
        errorMessage: { zipCode: "enter correct zipcode" }
      });
    } 
    if (this.state.destination === '') {
      isError = true;
      this.setState(prev => ({
        ...prev,
        error: true,
        errorMessage: { ...prev.errorMessage, destination: "enter correct destination" }
      }))
    }  if(!isError){
      //add else if for validating other fields (if any)
      this.setState(prevState => ({
        activeStep: prevState.activeStep + 1,
        error: false,
        errorMessage: {}
      }));
    }
  ...

Mui Textfield

          <TextField
              error={!!this.props.errorMessage.zipCode}
              required
              id="zip"
              name="zipCode"
              label="Zip / Postal code"
              fullWidth
              autoComplete="billing postal-code"
              value={this.props.zipCode}
              onChange={this.props.handleChange}
              helperText={
                this.props.errorMessage.zipCode &&
                this.props.errorMessage.zipCode
              }
            />

Mui Select use

<FormControl xs={12} sm={6} error={this.props.error}>
            <Select
              error={!!this.props.errorMessage.destination}
              label="enter cor dest"
              required
              value={this.props.destination}
              onChange={this.props.handleChange}
              input={<Input name="destination" />}
              displayEmpty
              name="destination"
            >
              <MenuItem value="">
                <em>Select Your Airport *</em>
              </MenuItem>
              <MenuItem name="SAN" value={"SAN"}>
                San Diego International Airport
              </MenuItem>
              <MenuItem name="LAX" value={"LAX"}>
                Los Angeles International Airport
              </MenuItem>
            </Select>
            <FormHelperText>
              {this.props.errorMessage.destination}
            </FormHelperText>
          </FormControl>
like image 113
gdh Avatar answered Oct 21 '22 15:10

gdh