Skip to content

[PROPOSITION NEW FEATURE] Select2 with data list obtained via AJAX #80

@shawe

Description

@shawe

This is not an issue of an error.

May it be possible to implement filters that are populated via AJAX, as documented for Select2, so that large amounts of data can be used, but without negatively affecting the user experience?

I have some working on editing small forms, and I think this way would be good for certain cases where some filters could have a lot of data but its populated on demand when needed.

I can share this with you, or colaborate with your repo to add this support if you are interested.

I do it this way to have an initial support outside laravel livewire tables, and seems to be responsiveness. But not in laravel liverwire tables, an looking for a possible existing filter found your repo, and I think that could be a good extra for your advanced filters. But maybe not implement as I'm sharing with you to can have any usable samples.

  • The default selected items, are auto-loaded with an AJAX call by ID.
  • The others items are loaded with a preview of APP_PAGINATION items, and you can write to get other items, and scroll down to load more pages.

For example:

  • Admin route:
Route::get('users/json/{search?}', [UserController::class, 'json'])->name('admin.users.json');
  • Admin Controller: app/Http/Controllers/Admin/UserController.php
    /**
     * Get users as JSON.
     *
     * @param string $search
     * @return JsonResponse
     */
    public function json(string $search = ''): JsonResponse
    {
        if (empty($search)) {
            $search = request()->input('term');
        }
        $items = DB::table(User::getTableName())
            ->select('id AS id', 'name AS text')
            ->where('name', 'like', '%' . $search . '%')
            ->orWhere('id', $search)
            ->orderBy('name')
            ->paginate(env('APP_PAGINATION', 10), ['*'], 'page', request()->input('page', 1));
        return response()->json([
            'pagination' => [
                'more' => $items->hasMorePages(),
                'last_page' => $items->lastPage(),
                'current_page' => $items->currentPage(),
            ],
            'results' => $items->items(),
        ]);
    }
  • Select component: resources/views/components/form/select.blade.php
<div class="form-group">
    {!! Form::label($name, $label, ['class' => 'control-label fw-bold']) !!}
    @php
        $attrs = $attrs ?? [];
        if(isset($placeholder)) {
            $attrs = array_merge(['placeholder' => $placeholder ?? ''], $attrs);
        }
        if (in_array('multiple', $attrs)) {
            $name = preg_match('/\[\]$/', $name) ? $name : $name . '[]';
        }
        $options = array_merge(['' => __('Select one item')], $options ?? []);
    @endphp
    {!! Form::select($name, $options, $value, array_merge(['class' => 'form-select select2'], $attrs)); !!}
    @error($name)
        <span class="text-danger">{{ $message }}</span>
    @enderror
</div>

@once
    @push('css')
        <link rel="stylesheet" href="{{ asset('build/extensions/select2/css/select2.min.css') }}">
        <link rel="stylesheet"
              href="{{ asset('build/extensions/select2-bootstrap-5-theme/select2-bootstrap-5-theme.min.css') }}">
    @endpush

    @push('js')
        <script src="{{ asset('build/extensions/select2/js/select2.min.js') }}"></script>
        @if (file_exists(public_path('build/extensions/select2/js/i18n/' . app()->getLocale() . '.js')))
            <script src="{{ asset('build/extensions/select2/js/i18n/' . app()->getLocale() . '.js') }}"></script>
        @endif
    @endpush
@endonce

@push('js')
    <script>
        $(document).ready(function () {
            $('#{{ $id }}').select2({
                theme: 'bootstrap-5',
                width: $(this).data('width') ? $(this).data('width') : $(this).hasClass('w-100') ? '100%' : 'style',
                closeOnSelect: false,
                placeholder: {
                    id: '-1', // the value of the option
                    text: '{{ __('Select one item') }}'
                },
                @if (in_array('multiple', $attrs))
                    cache: true,
                    tags: true,
                    allowClear: true,
                    tokenSeparators: [',', ' '],
                @endif
                @if ($url)
                ajax: {
                    url: '{{ $url }}',
                    dataType: 'json',
                    delay: 250,
                    data: function (params) {
                        return {
                            term: params.term || '',
                            page: params.page || 1
                        };
                    },
                    processResults: function(data, params) {
                        params.page = params.page || 1;

                        return {
                            results: data.results,
                            pagination: {
                                more: data.pagination.last_page != params.page
                            }
                        };
                    },
                    cache: true,
                },
                @endif
            });

            {{-- INSERT DATA BY DEFAULT --}}
            @if (!empty($value))
                let itemSelect = $('#{{ $id }}');
                {{-- INSERT SINGLE DATA --}}
                @if (is_string($value))
                    $.ajax({
                        type: 'GET',
                        url: '{{ $url . '/' . $value }}',
                    }).then(function (data) {
                        data = data.results.length > 0 ? data.results[0] : data.results;
                        var option = new Option(data.text, data.id, true, true);
                        itemSelect.append(option).trigger('change');
                    });
                @endif
                {{-- INSERT MULTIPLE DATA --}}
                @if (is_array($value))
                    @foreach($value as $item_id)
                        $.ajax({
                            type: 'GET',
                            url: '{{ $url . '/' . $item_id }}',
                        }).then(function (data) {
                            data = data.results.length > 0 ? data.results[0] : data.results;
                            var option = new Option(data.text, data.id, true, true);
                            itemSelect.append(option).trigger('change');
                        });
                    @endforeach
               @endif
            @endif
        });
    </script>
@endpush

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions