Skip to content
Advertisement

Unable to show helperText once we click on the Submit button using reactjs?

I’m new to this dropdown validations, i want to show helpertext bottom of the dropdown when the dropdown field is selected (or) we have selected but again selected the -1 menu item value. Here I’m facing few issues.

  1. Couldn’t able to show red color for helpertext.

  2. For me, it is showing error in line #50 but in Sandbox it is not showing any error.

  3. While submitting form with empty dropdown field(-1) then it should show helpertext.

I could not able to show the Helper Text while submitting the form.

Here i have tried in this way:

class Sample extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      channel: -1,
      sports: -1,
      movie: ""
    };
  }
  handleChange = (e: any) => {
    this.setState({ channel: e.target.value });
  };
  handleSports = (e: any) => {
    this.setState({ sports: e.target.value });
  };

  handleMovie = (e: any) => {
    this.setState({ movie: e.target.value });
  };

  Valid = () => {
    const errors = { channel: "", sports: "", movie: "" };
    if (!this.state.channel) {
      errors.channel = "Please select channel";
    }
    if (!this.state.sports) {
      errors.sports = "select Sports";
    }
    if (!this.state.movie) {
      errors.movie = "select movie";
    }
    return {
      errors,
      isSubmit: Object.keys(errors).length === 0
    };
  };

  handleSubmit = (e: any) => {
    e.preventDefault();
    const data = {
      channel: this.state.channel,
      sports: this.state.sports,
      movie: this.state.movie
    };
    console.log(data);
  };

  render() {
    const { errors, isSubmit } = this.Valid();
    return (
      <>
        <FormControl>
          <Select
            defaultValue={-1}
            onChange={this.handleChange}
            displayEmpty
            inputProps={{ "aria-label": "Without label" }}
          >
            <MenuItem value={-1}>Select Channel</MenuItem>
            <MenuItem value={10}>Sports</MenuItem>
            <MenuItem value={20}>Entertainment</MenuItem>
          </Select>
          {!this.state.channel ? (
            <FormHelperText>{errors.channel}</FormHelperText>
          ) : null}
        </FormControl>
        {this.state.channel === 10 ? (
          <div>
            <FormControl>
              <Select
                defaultValue={-1}
                onChange={this.handleSports}
                displayEmpty
                inputProps={{ "aria-label": "Without label" }}
              >
                <MenuItem value={-1}>Select </MenuItem>
                <MenuItem value={10}>Star sports 1</MenuItem>
                <MenuItem value={20}>Star sports 2</MenuItem>
              </Select>
              {!this.state.sports ? (
                <FormHelperText>{errors.sports}</FormHelperText>
              ) : null}
            </FormControl>
          </div>
        ) : this.state.channel === 20 ? (
          <div>
            <FormControl>
              <Select
                defaultValue={-1}
                onChange={this.handleMovie}
                displayEmpty
                inputProps={{ "aria-label": "Without label" }}
              >
                <MenuItem value={-1}>Select</MenuItem>
                <MenuItem value={10}>Star Movies</MenuItem>
                <MenuItem value={20}>ABC</MenuItem>
              </Select>
              {!this.state.movie ? (
                <FormHelperText>{errors.movie}</FormHelperText>
              ) : null}
            </FormControl>
          </div>
        ) : null}
        <div>
          <Button disabled={isSubmit} onClick={this.handleSubmit}>
            Submit
          </Button>
        </div>
      </>
    );
  }
}
export default Sample;

Here is the sample

Can anyone please help me with this query?

Advertisement

Answer

Issue 1

Couldn’t able to show red color for helpertext.

You can color the text red by using the error prop on the FormControl or FormHelperText. The FormControl actually provides a context to the fields it wraps so it’s easiest to set the error prop there.

<FormControl error={!!errors?.channel}>
  ...
  {errors?.channel && <FormHelperText>{errors.channel}</FormHelperText>}
</FormControl>

Issue 2

For me, it is showing error in line #50 but in Sandbox it is not showing any error.

Unfortunately without any more context about what line 50 consists of locally for you there’s not much to resolve here. Feel free to update your question with more details, perhaps a code snippet of the offending line and the copy of the error message.

Issue 3

While submitting form with empty dropdown field(-1) then it should show helpertext.

Here I think a bit of a refactor is in order.

  1. Disable the first “placeholder” select option, it should really be selectable, but it can still be the defaultValue. What this means is that once a valid option is selected, only other valid options can be selected, it can never select the disabled option. This also means that the field is only invalid while a user has not chosen any option.

    <MenuItem disabled value={-1}>
      Select Channel
    </MenuItem>
    <MenuItem value={10}>Sports</MenuItem>
    <MenuItem value={20}>Entertainment</MenuItem>
    
  2. You’ve 2 levels of select fields. When a user changes the root channel state, reset the movie and sports states.

  3. Change the state shape to include some error/validation object that will hold errors and form submittable status. Reset when state updates.

    componentDidUpdate(prevProps, prevState) {
      // If channel updated then reset "nested" select values
      if (prevState.channel !== this.state.channel) {
        this.setState({
          sports: null,
          movie: null
        });
      }
    
      // Clear error state upon change, next validation to occur next submit attempt
      if (!prevState.validation.canSubmit && !this.state.validation.canSubmit) {
        this.setState({
          validation: {
            canSubmit: true
          }
        });
      }
    }
    
  4. Add name attributes to the select inputs to be accessed in the onChange handlers; these should match the names in state. Also, because you have a name, you can reduce the onChange handlers down to a single input change handler.

    <Select
      defaultValue={-1}
      onChange={this.handleChange}
      displayEmpty
      inputProps={{ "aria-label": "Without label" }}
      name="channel" // <-- add name attribute (add for other inputs as well)
    >
    

handler

    handleChange = (e: any) => {
      const { name, value } = e.target;
      this.setState({ [name]: value } as Pick<
        State,
        "channel" | "movie" | "sports"
      >);
    };
  1. Since it isn’t possible to select sports AND movie, use some branching logic to validate one or the other depending on channel, i.e. if sports is expected to have a value then movie will never have a value, and vice-versa.

    Valid = ({ channel, sports, movie }): Validation => {
      const errors: Errors = {};
    
      if (!channel) {
        errors.channel = "Please select channel";
      } else {
        if (channel === 10) {
          if (!sports) {
            errors.sports = "select Sports";
          }
        } else {
          if (!movie) {
            errors.movie = "select movie";
          }
        }
      }
    
      return {
        errors,
        canSubmit: !Object.keys(errors).length
      };
    };
    
  2. Move the form validation to the onSubmit handler, disable the submit button if the validation “canSubmit” status is false.

    handleSubmit = (e: any) => {
      e.preventDefault();
      const { channel, movie, sports } = this.state;
    
      const validation = this.Valid({ channel, movie, sports });
    
      if (validation.canSubmit) {
        window.alert("SUBMIT!!");
      } else {
        console.log("errors", validation.errors);
        this.setState({ validation });
      }
    };
    

Edit unable-to-validate-dropdown-once-we-click-on-the-submit-button-using-reactjs

Fullcode

I’ll apologize ahead of time as I have very little to no typescript experience. Just some quick reading up on syntax. The following has zero errors/warnings in the linked sandbox.

import React from "react";
import {
  FormControl,
  Select,
  MenuItem,
  FormHelperText,
  Button
} from "@material-ui/core";

interface Props {}

interface Errors {
  channel?: string;
  sports?: string;
  movie?: string;
}

interface Validation {
  errors?: Errors;
  canSubmit: boolean;
}

interface State {
  channel: number;
  sports: number;
  movie: string;
  validation: Validation;
}

class Sample extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      channel: null,
      sports: null,
      movie: null,
      validation: {
        canSubmit: true
      }
    };
  }

  componentDidUpdate(prevProps, prevState) {
    // If channel updated then reset "nested" select values
    if (prevState.channel !== this.state.channel) {
      this.setState({
        sports: null,
        movie: null
      });
    }

    // Clear error state upon change, next validation to occur next submit attempt
    if (!prevState.validation.canSubmit && !this.state.validation.canSubmit) {
      this.setState({
        validation: {
          canSubmit: true
        }
      });
    }
  }

  handleChange = (e: any) => {
    const { name, value } = e.target;
    this.setState({ [name]: value } as Pick<
      State,
      "channel" | "movie" | "sports"
    >);
  };

  Valid = ({ channel, sports, movie }): Validation => {
    const errors: Errors = {};

    if (!channel) {
      errors.channel = "Please select channel";
    } else {
      if (channel === 10) {
        if (!sports) {
          errors.sports = "select Sports";
        }
      } else {
        if (!movie) {
          errors.movie = "select movie";
        }
      }
    }

    return {
      errors,
      canSubmit: !Object.keys(errors).length
    };
  };

  handleSubmit = (e: any) => {
    e.preventDefault();
    const { channel, movie, sports } = this.state;

    const validation = this.Valid({ channel, movie, sports });

    if (validation.canSubmit) {
      window.alert("SUBMIT!!");
    } else {
      console.log("errors", validation.errors);
      this.setState({ validation });
    }
  };

  render() {
    const {
      validation: { canSubmit, errors }
    } = this.state;

    return (
      <>
        <FormControl error={!!errors?.channel}>
          <Select
            defaultValue={-1}
            onChange={this.handleChange}
            displayEmpty
            inputProps={{ "aria-label": "Without label" }}
            name="channel"
          >
            <MenuItem disabled value={-1}>
              Select Channel
            </MenuItem>
            <MenuItem value={10}>Sports</MenuItem>
            <MenuItem value={20}>Entertainment</MenuItem>
          </Select>
          {errors?.channel && <FormHelperText>{errors.channel}</FormHelperText>}
        </FormControl>

        {this.state.channel === 10 && (
          <div>
            <FormControl error={!!errors?.sports}>
              <Select
                defaultValue={-1}
                onChange={this.handleChange}
                displayEmpty
                inputProps={{ "aria-label": "Without label" }}
                name="sports"
              >
                <MenuItem disabled value={-1}>
                  Select{" "}
                </MenuItem>
                <MenuItem value={10}>Star sports 1</MenuItem>
                <MenuItem value={20}>Star sports 2</MenuItem>
              </Select>
              {errors?.sports && (
                <FormHelperText>{errors.sports}</FormHelperText>
              )}
            </FormControl>
          </div>
        )}

        {this.state.channel === 20 && (
          <div>
            <FormControl error={!!errors?.movie}>
              <Select
                defaultValue={-1}
                onChange={this.handleChange}
                displayEmpty
                inputProps={{ "aria-label": "Without label" }}
                name="movie"
              >
                <MenuItem disabled value={-1}>
                  Select
                </MenuItem>
                <MenuItem value={10}>Star Movies</MenuItem>
                <MenuItem value={20}>ABC</MenuItem>
              </Select>
              {errors?.movie && (
                <FormHelperText error>{errors.movie}</FormHelperText>
              )}
            </FormControl>
          </div>
        )}

        <div>
          <Button disabled={!canSubmit} onClick={this.handleSubmit}>
            Submit
          </Button>
        </div>
      </>
    );
  }
}
export default Sample;
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement