Closed jdoklovic closed 4 years ago
@jdoklovic Have you tried using multiple?
👋 Thanks for using Material-UI!
We use GitHub issues exclusively as a bug and feature requests tracker, however, this issue appears to be a support request.
For support, please check out https://material-ui.com/getting-started/support/. Thanks!
If you have a question on StackOverflow, you are welcome to link to it here, it might help others. If your issue is subsequently confirmed as a bug, and the report follows the issue template, it can be reopened.
@jdoklovic It's supported, you would need to:
getOptionLabel
to render the correct input value (the full string)renderOption
to render the correct option value (the incremental change string)@mikkopursuittechnology Thanks for the interest in the topic :). I believe multiple
isn't necessary.
@oliviertassinari I'm not sure I'm following this. perhaps I'm misunderstanding what getOptionLabel and renderOption are used for. I assumed getOptionLabel was to display the labels in the drop down and renderOption was to render the option in the dropdown.
It seems like you're saying getOptionLabel is used to determine the new value for the input?
@oliviertassinari Sorry, one last question... I have this somewhat working, but was just wondering if it's expected that getOptionLabel is called multiple times for the same option?
I'm seeing that when I click an option, getOptionLabel gets called 3 times.
I was also looking to make something like this.
class ExpressionBuilder extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: this.props.value ? this.props.value : '',
fakeValue: ''
}
this.filterOptions = this.filterOptions.bind(this);
this.suggestItems = this.suggestItems.bind(this);
this.handleChange = this.handleChange.bind(this);
}
suggestItems(value) {
/**
* return items to suggest, based upon context
*/
return ['blah', 'nomore'];
}
getCurrentWord(text) {
const words = text.split(" ");
return words[words.length - 1];
}
filterOptions(options, { inputValue }) {
const items = this.suggestItems(inputValue);
let currentWord = this.getCurrentWord(inputValue);
const matched_chars = items.filter((char) => {
return char.indexOf(currentWord.toLowerCase()) !== -1;
});
return matched_chars;
}
handleChange(event) {
const value = event.target.value;
this.setState({
inputValue: value
});
/**
* Small hack to reset the internal state of the material autocompleter,
* to allow it to suggest the previously suggested value,
* if the input in totally wiped, and also reset highlight
*/
if (value === '') {
this.setState({
fakeValue: ''
});
}
}
render() {
return (
<Autocomplete
id="expression-builder"
disableClearable
options={filters}
autoHighlight={true}
value={this.state.fakeValue}
inputValue={this.state.inputValue}
filterOptions={this.filterOptions}
onChange={(event, newInputValue) => {
const words = this.state.inputValue.split(" ");
words.pop();
words.push(newInputValue);
const newWord = words.join(" ");
this.setState({
inputValue: newWord
});
}}
freeSolo={true}
style={{ width: 300 }}
renderInput={(params) => {
return <TextField className="expressionBuilderField" {...params} variant="outlined" onChange={this.handleChange}/>
}}
/>
);
}
}
export default ExpressionBuilder;
This implementation worked for me!
class ExpressionBuilder extends React.Component { constructor(props) { super(props); this.state = { inputValue: this.props.value ? this.props.value : '', fakeValue: '' } this.filterOptions = this.filterOptions.bind(this); this.suggestItems = this.suggestItems.bind(this); this.handleChange = this.handleChange.bind(this); } suggestItems(value) { /** * return items to suggest, based upon context */ return ['blah', 'nomore']; } getCurrentWord(text) { const words = text.split(" "); return words[words.length - 1]; } filterOptions(options, { inputValue }) { const items = this.suggestItems(inputValue); let currentWord = this.getCurrentWord(inputValue); const matched_chars = items.filter((char) => { return char.indexOf(currentWord.toLowerCase()) !== -1; }); return matched_chars; } handleChange(event) { const value = event.target.value; this.setState({ inputValue: value }); /** * Small hack to reset the internal state of the material autocompleter, * to allow it to suggest the previously suggested value, * if the input in totally wiped, and also reset highlight */ if (value === '') { this.setState({ fakeValue: '' }); } } render() { return ( <Autocomplete id="expression-builder" disableClearable options={filters} autoHighlight={true} value={this.state.fakeValue} inputValue={this.state.inputValue} filterOptions={this.filterOptions} onChange={(event, newInputValue) => { const words = this.state.inputValue.split(" "); words.pop(); words.push(newInputValue); const newWord = words.join(" "); this.setState({ inputValue: newWord }); }} freeSolo={true} style={{ width: 300 }} renderInput={(params) => { return <TextField className="expressionBuilderField" {...params} variant="outlined" onChange={this.handleChange}/> }} /> ); } } export default ExpressionBuilder;
This implementation worked for me!
Nice solution, this exactly what I needed. Thank you
Summary 💡
I have an autocomplete that parses the input and provides different sets of options depending where you are in the text. Think about this like autocompleting a "query" field:
Currently I have the options displaying properly with free solo and it "works" if I ignore the options and just keep typing, but as soon as I click on an option it replaces the entire value with the option clicked.
Currently I'm just using the Autocomplete component so I'm not sure if there's a way to swipe the onClick handler of the options and get it to do what I want and maintain all the other functionality. I haven't tried useAutocomplete yet, so I'm not sure if that would help.
Examples 🌈
essentially I want to build something like this: