wireui / wireui

TallStack UI components
https://v1.wireui.dev
MIT License
1.37k stars 166 forks source link

No options shown in x-select, when more than 10 options provided #465

Open eswachsman opened 1 year ago

eswachsman commented 1 year ago

Describe the bug When adding more than 10 options (in my case, a collection of models) to the <x-select> component, no options are shown. image

To Reproduce Steps to reproduce the behavior:

  1. Create a select, using a collection of more than 10 models as the :options parameter
  2. Try to open the select. It says Empty options

Expected behavior Expected behavior is that it should show all options. Or at least provide a limit parameter so I can set my own limit.

Screenshots or Videos image image

Dependencies

Desktop (If applicable, please complete the following information):

joaopalopes24 commented 1 year ago

Hello @eswachsman, how are you? I just ran the test here and it worked normally. Can you send me a print of the data collection that the $strategies variable is returning?

joaopalopes24 commented 1 year ago

If indeed it is returning an eloquent collection, try removing the strategy. in the option-label and in the option-value.

joaopalopes24 commented 1 year ago

@eswachsman Does the error persist?

eswachsman commented 1 year ago

Yes, the provided solutions did not help

joaopalopes24 commented 1 year ago

Have you tried the solution I mentioned above?

labomatik commented 1 year ago

I experienced the same issue with my customers but doesn't seem to be related to the number of option. I requested one of my customers to try the select at this location: https://livewire-wireui.com/docs/select and he confirmed that the select are displaying 'Empty Options' while data is available

We got issues with Windows and Apple's New OS (Ventura) with chrome (v 107.0.5304.110)

stephancasas commented 1 year ago

I find that this issue occurs most frequently when values provided to the <x-select /> are dependent upon other mutable Livewire properties (e.g. user can select a style which then populates <x-select /> with size options).

Options are populated as <li> members in the Popover component of <x-select />. These are rendered by Alpine — not Livewire. However, the dynamic data provided to them is, of course, dependent upon Livewire. If that doesn't make sense, consider the way that synchronization occurs between WireUi, Livewire, and Alpine:

https://github.com/wireui/wireui/blob/cde361903acbfd6c09d639374204cb165901ce84/ts/components/select/index.ts#L57-L59

For both attribute-provided options and slot-provided options, a MutationObserver is registered. This is done to watch for changes that Livewire makes to the elements which contain the available options. Here's the observer registered for slot-provided options:

https://github.com/wireui/wireui/blob/cde361903acbfd6c09d639374204cb165901ce84/ts/components/select/index.ts#L216-L227

As you can see, when Livewire makes a change to the slot, the syncSlotOptions() function runs, and Alpine will render the new <li> items for the popover.


I suspect that this approach likely fits most use cases. However, if the mutable property from which an <x-select />'s dependent options are derived also mutates markup which contains the <x-select /> itself, there exists the possibility that MorphDOM will apply changes in such a way the MutationObserver completely misses Livewire's activity, and thus the <li> option elements never populate.

In my own projects, I've patched this by publishing WireUi's views, and then adding an Alpine event listener to select.blade.php:

<div {{ $attributes->only(['class', 'wire:key'])->class('relative') }}
     x-data="wireui_select({
         @if ($attributes->wire('model')->value()) wireModel: @entangle($attributes->wire('model')), @endif
     })"
     x-props="{
        asyncData:    @toJs($asyncData),
        optionValue:  @toJs($optionValue),
        optionLabel:  @toJs($optionLabel),
        optionDescription: @toJs($optionDescription),
        hasSlot:     @boolean($slot->isNotEmpty()),
        multiselect: @boolean($multiselect),
        searchable:  @boolean($searchable),
        clearable:   @boolean($clearable),
        readonly:    @boolean($readonly || $disabled),
        placeholder: @toJs($placeholder),
        template:    @toJs($template),
    }"
     @wireui-select-should-update.window="!asyncData.api && (config.hasSlot ? syncSlotOptions() : syncJsonOptions())">
    {{-- ... --}}
</div>

Then, I use one of Livewire's updatedX() hooks to fire an event — deferred — after I'm sure that Livewire is done applying its DOM mutations:

public function updatedStyle() {
  $this->emit('wireui-select-should-update');
}
Livewire.on('wireui-select-should-update', () =>
  setTimeout(() =>
    window.dispatchEvent(new CustomEvent('wireui-select-should-update')),
  ),
);

It's a bit of a dirty patch but it works for the edge cases where I need it.


As for a fix, I'd be interested in hearing @PH7-Jack's thoughts on whether this is actually a bug, or if it's something that we should abate on our own by rearranging problematic markup or through strategic use of wire:ignore and x-show instead of @if and other conditional Blade directives.

stephancasas commented 1 year ago

I have opened a PR for what I believe seems to be causing this issue. Please feel free to review test changes in your own environment.