If you create a Lookup component for letting the user find an item to add to a list, and you want to prevent the user from adding the same item twice, you might implement that by doing something like this:
- Maintain an array with the items the user has already chosen
- Bind :menu-items to a computed property that takes the lookup results and filters out already-chosen items
- When an item is selected, add it to the array of chosen items
However, if you do this, the Lookup fails to close when you select an item. If you had typed any text into the input, that text is wiped away as well. Both of these bugs are caused by the fact that the selected item has been removed:
- The input text is wiped because the watcher on the modelValue sees a new value that is not null (it's the ID of the selected item), but it can't find the corresponding item (since it's been removed), so it sets inputValue to the empty string
- The menu does close initially on selection (done by the Menu component), but then the watcher on the menuItems prop fires when the selected item is removed. It concludes that the input value is not equal to the label of the selected item (because it can't find that item anymore), and that there are more than 0 menu items, so it (re)opens the menu.
The first of these appears to happen in the "wrong" order, because modelValue is updated before the selected item is removed, but that's because Vue calls watchers and computed property functions asynchronously (on nextTick). So by the time the modelValue watcher runs, the menuItems prop has also been updated.
See this code for a simple example, and try it out here. This is based on a real-life example in WikiLambda, discovered by @JKieserman.
I'm not sure how we should address this. To some extent, this is a reasonable use case, and one of the bugs is triggered by the somewhat tricky logic in the menuItems watcher. On the other hand, removing the selected item right when it's selected is bound to cause problems, because it blows up any logic that might want to look at the properties of the item we just selected.
Some ideas for solving one or both of these issues:
- If the menuItems watcher only tried to open/close the menu if the number of items changed from zero to nonzero / from nonzero to zero, that would fix the "menu fails to close" issue. However, we might need this behavior or something like it for cases where the user types something, chooses an item, and then types something else.
- If the menuItems watcher had some other way of knowing that we just made a selection, it could use that instead of checking whether the input matches the selected item. Perhaps on selection we could do something like justSelected = true; nextTick( () => justSelected = false );
- If we had an update:selected handler on the Menu that, before emitting Lookup's own update:modelValue first updated inputValue, updating the input correctly for the selected item would work even if the selected item then changes or disappears.
- If the modelValue watcher can't find the selected item, it could leave inputValue alone instead of blanking it