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.
Couldn’t able to show red color for helpertext.
For me, it is showing error in line #50 but in Sandbox it is not showing any error.
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;
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.
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>
You’ve 2 levels of select fields. When a user changes the root
channel
state, reset themovie
andsports
states.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 } }); } }
Add
name
attributes to the select inputs to be accessed in theonChange
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" >); };
Since it isn’t possible to select
sports
ANDmovie
, use some branching logic to validate one or the other depending onchannel
, i.e. ifsports
is expected to have a value thenmovie
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 }; };
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 }); } };
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;