lowcrawler
lowcrawler

Reputation: 7549

Material UI select not showing label

I'm having a heck of a time with Material UI's "Select" - about 10 hours into trying to get one working the way I'd like. I'd really appreciate some help.

This question is related to a previous one: Select MenuItem doesn't show when JSX saved to state and I suspect that if the "why" were answered on that, I might get a better idea of what's going on.

What I'm trying to accomplish is having a Select that does the following normal things:

These shouldn't be hard tasks, but I can't for the life of me get it. It' rather embarrassing.

Either way, I've currently got this JSX - effectively a cut-and-paste from the material ui demos with a map for the MenuItems:

<FormControl className={classes.formControl}>
<InputLabel htmlFor={this.props.label}>{this.props.label}</InputLabel>
<Select
    value={this.state.selectLabel}
    onChange={this.handleSelectChange}
    inputProps={{
        name: 'selectLabel',  
        id: this.props.label,
    }}
>
{this.props.value.map(valueLabelPair =>
                <MenuItem
                    key={this.props.XMLvalue + "_" + valueLabelPair.label}
                    value={valueLabelPair.value}
                >
                    {valueLabelPair.label}
                </MenuItem>
            )}
</Select>
</FormControl>

the handleSelectChange looks like this -- again, exactly the same as the material UI demo.

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

This kind of works except the console gives me the following error:

Failed prop type: The prop value is marked as required in SelectInput, but its value is undefined.

and the selected option and question label go on top of each other after you click away, like so: selected option and question label on top of each other

Further, if I try to add in this code (in the componentDidMount function) with the goal of being able to pass in the 'selected'/default option...

componentDidMount() {
    for (var i = 0; i < this.props.value.length; i++) {
        if(this.props.value[i].selected) {
            // *works* console.log("selected found: " + this.props.value[i].label);
            this.setState({selectLabel:this.props.value[i].label});
        }
    }
}

it does not update the give me a default answer and also gives me the following additional error in the console (in addition to all issues above):

Warning: A component is changing an uncontrolled input of type hidden to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

What am I missing?

Upvotes: 6

Views: 19937

Answers (3)

Malikjan Nadaf
Malikjan Nadaf

Reputation: 1

Add class radio_label :

<FormControlLabel value="male" label="Male" control={<Radio />}  className="radio_label"/>

Add css property :

.radio_label{
    color:black
}

Upvotes: 0

lowcrawler
lowcrawler

Reputation: 7549

I am unsure as to why the above solution did not work.

However, I rebuilt the Select to return "option" elements instead of "MenuItem" elements with the following function:

buildSelectOptions(optionsPairs) {  // note, this references props and blank option could be split out for reuse
    var JSX_return = [];

    if (this.props.includeBlank && this.props.includeBlank === true) {
        JSX_return.push(<option key="nada" value="" />);
    }

    for (var optionLabel in optionsPairs) {
        JSX_return.push(<option key={optionLabel} value={optionsPairs[optionLabel]}>{optionLabel}</option>);
    }
    return JSX_return;
}

My render now looks like this:

<FormControl className={classes.formControl}>
    <InputLabel htmlFor="age-native-simple">{this.props.label}</InputLabel>
    <Select
        native
        value={this.state.value}
        onChange={this.handleSelectChange('value')}
        inputProps={{
            name: this.props.label,
            id: this.props.id,
          }}
    >
        {this.buildSelectOptions(this.props.options)}

    </Select>
    <FormHelperText>{this.props.helperText}</FormHelperText>
</FormControl>

And the event handler looks like this:

handleSelectChange = name => event => {  //FUTURE: combine the handlers  (or split out question types to sub-components)
    this.setState({ [name]: event.target.value },
        () => this.props.stateChangeHandler(this)
    );
};

the props passed to this object look like this:

{
    "key": "test4",
    "id": "test4",
    "label": "Question Label 4",
    "XMLValue": "XMLQ4",
    "type": "DropDown",
    "includeBlank": true,
    "helperText": "PCodes FTW!",
    "options": {
        "No oe": "NA",
        "My1": "One",
        "My2": "Two",
        "My3": "three"
    },
    "value": "One"
}

One of the key concepts for me was to learn that the value field on the Select element should be pointed at an item in this.state. Then, the onChange needs to pass the name of that state variable (which, confusingly, I have named 'value') to the eventHandler function.

The double arrow function header (curried function) of the handleSelectChange function still confuses me... (I don't understand how the 'event' property is getting there, given I'm calling this function with a single parameter)... but this works for now and I can try to refactor into syntax I am comfortable with (ie: function(a, b) { *do something* } ) at some future date. (yeah....)

Upvotes: 0

Shailendra Nagori
Shailendra Nagori

Reputation: 1

Just define selectLabel into constructor:

constructor () {
    super()
    this.state = {
        selectLabel:'',
    }
}

Upvotes: 4

Related Questions