Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Autocomplete]filter not work correctly after selecting option once #19797

Closed
2 tasks done
thundermiracle opened this issue Feb 21, 2020 · 19 comments
Closed
2 tasks done
Labels
component: autocomplete This is the name of the generic UI component, not the React module! discussion

Comments

@thundermiracle
Copy link

  • The issue is present in the latest release.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Current Behavior 😯

filterOptions is not correctly working after select option once.

Expected Behavior 🤔

after click to select the option, filter will still work fine.

Steps to Reproduce 🕹

  1. Click to select one option
  2. Re-open the Autocomplete, add a space, filter works well. Then delete the space, all options are displayed instead of the only filtered one.

And every sample in documentation has the same problem.

autocomplete

Your Environment 🌎

Tech Version
Material-UI v4.9.2
Material-UI-lab v4.0.0-alpha.43
React v16.12.0
@thundermiracle
Copy link
Author

I think this line in useAutocomplete causes this issue.

const inputValueIsSelectedValue =
    !multiple && value != null && inputValue === getOptionLabel(value);

{ inputValue: inputValueIsSelectedValue ? '' : inputValue },

After selected the option, inputValueIsSelectedValue becomes true forever and '' will be passed into the filterOptions() so all options are displayed.

@oliviertassinari
Copy link
Member

Why should it behave differently? What's your use case?

@thundermiracle
Copy link
Author

Same input should display same option IMO.
Input The Shawshank Redemption first time displays only The Shawshank Redemption,
the second time will display all options which is confusing.
Perhaps empty inputValue is the normal behavior in Autocomplete, would you please add an option which can ignore inputValueIsSelectedValue check?

autocomplete-2

@thundermiracle
Copy link
Author

And we'd like to use Autocomplete in freeSolo mode.
User can input or select The Shawshank Redemption.
But he will aware that behaviors are different between input it and select it which is difficult to explain.

https://codesandbox.io/s/material-demo-o4nli

@oliviertassinari oliviertassinari added the component: autocomplete This is the name of the generic UI component, not the React module! label Feb 21, 2020
@oliviertassinari
Copy link
Member

I'm closing as the behavior looks expected. We would be happy to reconsider it when we have a compelling use case for a different behavior.

@thundermiracle
Copy link
Author

@oliviertassinari
The most common use case is making a search bar like google does.
And of course google search bar displays same options no matter I input The Shawshank Redemption or select The Shawshank Redemption from the drop down options.

I was trying to just load options once and achieve the same effect so I opened this issue.
So as the behavior is expected, I have to make async request after every change of inputValue and returns same options from server to achieve the same effect, am I right?

@oliviertassinari
Copy link
Member

So as the behavior is expected, I have to make async request after every change of inputValue and returns same options from server to achieve the same effect, am I right?

Exactly

@thundermiracle
Copy link
Author

@oliviertassinari
Thanks for your time!

@JohnRDOrazio
Copy link

JohnRDOrazio commented Apr 16, 2021

I find this behaviour very strange. I would expect the current selected option to show in the dropdown options. It is very strange for it not to show. I am using Autocomplete for compiling an address on a form. The "country" input has the list of all the countries in the world. I am using match-sorter to filter the options, since the list of countries is an array of objects with some information about each country (ISO 3166-1 alpha-2, ISO 3166-1 alpha-3, localized name in Italian, English name, etc.). If the current value in the backend database is stored as "Italia", I get the correct option from the array of objects by filtering the options array in the defaultValue property of the Autocomplete. Value is showing correctly in the field. However, with the value already in the field, if I then click on the field to get the dropdown, instead of seeing "Italia" as the selected option, I'm seeing "Afghanistan" as the first option. I'm also using these Autocomplete options: selectOnFocus, autoHighlight, autoComplete, autoSelect. Which means that if I tab out of the field after the dropdown opens, now "Afghanistan" becomes the new value instead of keeping the value "Italia".
Instead the currently selected option should be the inputValue string sent to the filterOptions - match-sorter so that the current selected option is the first one shown in the list. I don't see the reason for forcing the inputValue to an empty string? If I simply tab away from the field after opening the dropdown, without typing anything in the field, the current selected option should continue to be the current selected option.

In order to overcome this, I'm storing the current value in another hidden input field, then before returning the match-sorter I check whether inputValue is empty or not: if it's empty I force it's value back to the value of the hidden input field. This way I get my expected behaviour back: current selected option is showing as current selected option in the dropdown, and stays as the current selected option if I simply tab away.

@oliviertassinari
Copy link
Member

@JohnRDOrazio There is too much information in your previous comment, we can't act on it. Please ask for help on StackOverflow.

I get my expected behaviour back: current selected option is showing as current selected option in the dropdown, and stays as the current selected option if I simply tab away.

It sounds like you didn't provide a getOptionLabel prop.

@JohnRDOrazio
Copy link

I did provide a getOptionLabel prop. But since, as I see here, the inputValue is being forced to an empty string when the current input value is the current selected value, this means that the current selected value is never going to show in the dropdown. I don't see the reason for forcing inputValue to an empty string?
Here is an example of my use case: Country autocomplete demo
As you can see, when the value from the backend is "IT", the value in the input is correctly showing as "Italia". If you click in the input, instead of seeing "Italia" in the dropdown, you see "Afghanistan" as the first option. If you inspect the console, you can see inputValue = . I would expect instead to see inputValue = Italia, as I would have seen were I typing that value into the input, and I would expect to see "Italia" as the first and current selected option in the dropdown.

@oliviertassinari
Copy link
Member

oliviertassinari commented Apr 16, 2021

@JohnRDOrazio inputValue === '' in the filterOptions call to signal that no filters should be applied.

@JohnRDOrazio
Copy link

I find that to be an odd behaviour, resulting in unexpected results for users. Why should no filters be applied when there is an existing value in the input? No filters should be applied only when there is no value, not when there is an existing value, in my opinion. I have updated the example with a second autocomplete, demonstrating my fix for an expected behaviour:
https://codesandbox.io/s/material-demo-forked-ir1o4?file=/demo.js

If you click in the second autocomplete you will see that "Italia" is the current selected option, and if you click anywhere outside, when the dropdown closes the value in the input will stay "Italia". Whereas in the first example, since "Afghanistan" is the first selected option, if you click anywhere outside, when the dropdown closes "Afghanistan" will become the newly selected option, whereas the user probably didn't want to select any option different from the current value of the input.

@pedrohenriiz
Copy link

I'm facing almost the same issue as @thundermiracle and @JohnRDOrazio. I'm using the Autocomplete on some forms and with this behaviour, things goes a bit confusing for the users. One of my cases is on phone operators. If the user select an option, removes one letter and press that letter again, all values appears. It would be better if shows only the option that matches with that one that they selected.

@worldsayshi
Copy link

worldsayshi commented May 6, 2021

I have this problem as well.

Use case

  1. You are using the autocomplete to look for a specific product in a huge catalog. Some products have the same or similar names.
  2. When selected you see a small summary of the product on display in a separate component.
  3. Sometimes you realise that you picked the wrong product and need to go back to the dropdown to look at the list of similarly named products.
  4. The list now contains the full product catalog. You need to retype the text to get similar things. While doing that you might stumble into a related issue (see below).

Workaround

With trial and error I found a solution that works as I want. But the fact it works seems quite coincidental.

I'm using @material-ui/core 4.11.2 and @material-ui/lab 4.0.0-alpha.57.

const WorkaroundComponent = () => {
    const [value, setValue] = useState<string>("");
    return (
        <Autocomplete options={[{name: "aa"}, {name: "aab"}, {name: "baab"}, {name: "not interesting result"}]}
                      renderOption={({name}) => (name)}
                      getOptionLabel={({name}) => (name)}
                      value={null}
                      onBlur={() => {
                          let theValue = value;
                          setValue("");
                          setValue(theValue);
                      }}
                      inputValue={value}
                      onInputChange={((event, value) => setValue(value))}
                      renderInput={(params) => (
                          <TextField name="search"
                                     margin="dense"
                                     size="small"
                                     variant="outlined"
                                     label="Search"
                                     type="text"
                                     {...params}
                          />
                      )}
        />
    );
}

Related issue/behavior

A related slightly surprising behavior is that the full result list also "sticks" to the selected text.

Let's remove the workaround and just turn it into a controlled component:

const RelatedIssueComponent = () => {
    const [value, setValue] = useState<string>("");
    return (
        <Autocomplete options={[{name: "aa"}, {name: "aab"}, {name: "baab"}, {name: "not interesting result"}]}
                      renderOption={({name}) => (name)}
                      getOptionLabel={({name}) => (name)}
                      //value={null}
                      /*onBlur={() => {
                          let theValue = value;
                          setValue("");
                          setValue(theValue);
                      }}*/
                      inputValue={value}
                      onInputChange={((event, value) => setValue(value))}
                      renderInput={(params) => (
                          <TextField name="search"
                                     margin="dense"
                                     size="small"
                                     variant="outlined"
                                     label="Search"
                                     type="text"
                                     {...params}
                          />
                      )}
        />
    );
}
  1. Type "aa",
  2. click outside the component
  3. Remove one letter and type it again
  4. You will still get the full list again.

If you want to look at the filtered list you need to "trick" the cache by removing the full text and then retype it.

@adirzoari
Copy link

@worldsayshi @pedrohenriiz @JohnRDOrazio
same issue.. did you find any solution?

@JohnRDOrazio
Copy link

@adirzoari until the maintainers decide that the current behaviour should be fixed, I am using this workaround:

  1. create a hidden input field the value of which will reflect the current value of the autocomplete component (if you have a defaultValue on the Autocomplete component, use the same defaultValue on the hidden input)
    <input type="hidden" defaultValue="IT" ref={countryInp} />
  2. create a ref for the hidden input field
    const countryInp = useRef();
  3. update the hidden input field's value in the onChange event for the Autocomplete
    onChange={(event, newValue) =>
      (countryInp.current.value = newValue["Alpha-2"])
    }
  4. using the filterOptions attribute of the Autocomplete component, force the inputValue back to expected value
    filterOptions={nationFilterOptionsFixed}
    
    /** and here is my nationFilterOptionsFixed function */
    const nationFilterOptionsFixed = (options, { inputValue }) => {
      console.log("inputValue = " + inputValue);
      if (inputValue === "") {
        inputValue = countryInp.current.value;
      }
      return matchSorter(options, inputValue, {
        keys: ["Paese", "Alpha-2", "Alpha-3"]
      }).slice(0, 7);
    };

You can see an example of my fix here: https://codesandbox.io/s/material-demo-forked-ir1o4?file=/demo.js

Of course in your own use case you may need a different approach...

@vishalnewton
Copy link

May be this is happening due to some objects may have same labels. we have to pass a unique identifier, for this pass the getOptionKey prop in Autocomplete like this

getOptionKey={(option) => option.hotelCode}

in my case some objects may have same labels but each object has a unique hotelCode

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: autocomplete This is the name of the generic UI component, not the React module! discussion
Projects
None yet
Development

No branches or pull requests

8 participants