Laravel-Backpack / CRUD

Build custom admin panels. Fast!
https://backpackforlaravel.com
MIT License
3.11k stars 890 forks source link

[Bug] crud.addFunctionToDataTablesDrawEventQueue('addOrRemoveCrudCheckedItem') is not called #5462

Closed zeknoss closed 6 months ago

zeknoss commented 7 months ago

Bug report

What I did

I have added the $this->crud->enableBulkActions(); to the corresponding controller.

What I expected to happen

I expected that when I tried to access crud.checkedItems it would yield me an array.

What happened

It says crud.checkedItems is undefined

What I've already tried to fix it

I ran the crud.addFunctionToDataTablesDrawEventQueue('addOrRemoveCrudCheckedItem') command in console and triggered an ajax call so that the addOrRemoveCrudCheckedItem queue entry would be run and it worked. But when I refreshed the page, I saw that this wasn't firing automatically.

Is it a bug in the latest version of Backpack?

After I run composer update backpack/crud the bug is it still there.

Backpack, Laravel, PHP, DB version

When I run php artisan backpack:version the output is:

PHP VERSION:

PHP 8.2.13 (cli) (built: Nov 24 2023 13:10:01) (NTS) Copyright (c) The PHP Group Zend Engine v4.2.13, Copyright (c) Zend Technologies with Zend OPcache v8.2.13, Copyright (c), by Zend Technologies

LARAVEL VERSION:

10.37.2.0

BACKPACK PACKAGE VERSIONS:

backpack/basset: 1.2.2 backpack/crud: 6.6.5 backpack/generators: v4.0.3 backpack/logmanager: v5.0.1 backpack/pro: 2.1.2 backpack/theme-coreuiv2: 1.2.3 backpack/theme-coreuiv4: 1.1.1 backpack/theme-tabler: 1.2.5

Note: it works on local. One detail I've noticed is that the script that is related to bulk operations is loaded inline in local environment and via a url when in production.

Thank you for your time

welcome[bot] commented 7 months ago

Hello there! Thanks for opening your first issue on this repo!

Just a heads-up: Here at Backpack we use Github Issues only for tracking bugs. Talk about new features is also acceptable. This helps a lot in keeping our focus on improving Backpack. If you issue is not a bug/feature, please help us out by closing the issue yourself and posting in the appropriate medium (see below). If you're not sure where it fits, it's ok, a community member will probably reply to help you with that.

Backpack communication channels:

Please keep in mind Backpack offers no official / paid support. Whatever help you receive here, on Gitter, Slack or Stackoverflow is thanks to our awesome awesome community members, who give up some of their time to help their peers. If you want to join our community, just start pitching in. We take pride in being a welcoming bunch.

Thank you!

-- Justin Case The Backpack Robot

zeknoss commented 7 months ago

Update

I have found out that this issue only happens until the user navigates to another page. When the user clicks to a pagination button below the table, it triggers the required script which then let's the bulk operation actions work properly.

karandatwani92 commented 7 months ago

Hey @zeknoss

It seems like you are trying to Create a New Operation With a Bulk Action .

Bulk delete is working, and the instructions say to mimic BulkDelete to build a custom one.

zeknoss commented 7 months ago

Hey @karandatwani92 That's actually the way I created my custom bulk action button. I followed the examples from Backpack itself.

In the bulk_delete.blade.php file it checks if the crud.checkedItems is defined and contains more than 0 entries. However, since the script that adds the whole operation regarding the bulk actions into the hooks queue is not called yet in the first page, checking and unchecking the items does not do anything. After I paginate to another page, it works.

You can see my code below:

@if ($crud->hasAccess('update'))
<div class="dropdown">
    <button class="btn btn-primary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
        Set Order Status
    </button>
    <ul class="dropdown-menu">
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="new">New</a></li>
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="processing">Processing</a></li>
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="succeeded">Succeeded</a></li>
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="cancelled">Cancelled</a></li>
    </ul>
</div>
@endif

@push('after_scripts')
@if ($crud->hasAccess('update'))
<div class="dropdown">
    <button class="btn btn-primary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
        Set Order Status
    </button>
    <ul class="dropdown-menu">
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="new">New</a></li>
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="processing">Processing</a></li>
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="succeeded">Succeeded</a></li>
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="cancelled">Cancelled</a></li>
    </ul>
</div>
@endif

@push('after_scripts')
<script>
    if (typeof setStatus != 'function') {
        if (typeof crud.checkedItems === 'undefined' || crud.checkedItems.length == 0)
        {
            new Noty({
                type: "warning",
                text: "<strong>{!! trans('backpack::crud.bulk_no_entries_selected_title') !!}</strong><br>{!! trans('backpack::crud.bulk_no_entries_selected_message') !!}"
            }).show();

            return;
        }
        $("[data-button-type=status-setter]").unbind('click').click(function() {
            setStatus(this);
        });

        function setStatus(button) {
            let status = $(button).attr('data-target');
            let message = "Are you sure you want change status to " + status + " for :number orders?";
            message = message.replace(":number", crud.checkedItems.length);

            if (!crud.checkedItems || crud.checkedItems.length === 0) return;

            swal({
                title: "{{ trans('backpack::base.warning') }}",
                text: message,
                icon: "warning",
                buttons: {
                    cancel: {
                        text: "{{ trans('backpack::crud.cancel') }}",
                        value: null,
                        visible: true,
                        className: "bg-secondary",
                        closeModal: true,
                    },
                    delete: {
                        text: "Proceed",
                        value: true,
                        visible: true,
                        className: "bg-primary",
                    }
                },
            }).then((value) => {
                if (value) {
                    const ajax_calls = [];
                    const status_route = "{{ url($crud->route) }}/set-status";

                    $.ajax({
                        url: status_route,
                        type: 'POST',
                        data: { entries: crud.checkedItems, status },
                        success: function(result) {
                            new Noty({
                                type: "success",
                                text: "Status updated for <strong>"+crud.checkedItems.length+"</strong> orders."
                            }).show();

                            crud.checkedItems = [];
                            crud.table.ajax.reload();
                        },
                        error: function(result) {
                            new Noty({
                                type: "danger",
                                text: "<strong>Status update failed</strong><br>Please try again."
                            }).show();
                        }
                    });
                }
            });
        }
    }
</script>
@endpush
pxpm commented 6 months ago

Hello @zeknoss Sorry for the delay here.

I think the issue is with your javascript. You moved the crud.checkedItems call to outside of the function, thus making the return statement an ilegal statement at that point.

Just by changing that check inside the function everything works as expectected: image

@if ($crud->hasAccess('update'))
<div class="dropdown">
    <button class="btn btn-primary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
        Set Order Status
    </button>
    <ul class="dropdown-menu">
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="new">New</a></li>
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="processing">Processing</a></li>
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="succeeded">Succeeded</a></li>
      <li><a class="dropdown-item" data-button-type="status-setter" href="#" data-target="cancelled">Cancelled</a></li>
    </ul>
</div>
@endif

@push('after_scripts')
<script>
    if (typeof setStatus != 'function') {

        $("[data-button-type=status-setter]").unbind('click').click(function() {
            setStatus(this);
        });

        function setStatus(button) {
            if (typeof crud.checkedItems === 'undefined' || crud.checkedItems.length == 0)
            {
            new Noty({
                type: "warning",
                text: "<strong>{!! trans('backpack::crud.bulk_no_entries_selected_title') !!}</strong><br>{!! trans('backpack::crud.bulk_no_entries_selected_message') !!}"
            }).show();

            return;
            }
            let status = $(button).attr('data-target');
            let message = "Are you sure you want change status to " + status + " for :number orders?";
            message = message.replace(":number", crud.checkedItems.length);

            if (!crud.checkedItems || crud.checkedItems.length === 0) return;

            swal({
                title: "{{ trans('backpack::base.warning') }}",
                text: message,
                icon: "warning",
                buttons: {
                    cancel: {
                        text: "{{ trans('backpack::crud.cancel') }}",
                        value: null,
                        visible: true,
                        className: "bg-secondary",
                        closeModal: true,
                    },
                    delete: {
                        text: "Proceed",
                        value: true,
                        visible: true,
                        className: "bg-primary",
                    }
                },
            }).then((value) => {
                if (value) {
                    const ajax_calls = [];
                    const status_route = "{{ url($crud->route) }}/set-status";

                    $.ajax({
                        url: status_route,
                        type: 'POST',
                        data: { entries: crud.checkedItems, status },
                        success: function(result) {
                            new Noty({
                                type: "success",
                                text: "Status updated for <strong>"+crud.checkedItems.length+"</strong> orders."
                            }).show();

                            crud.checkedItems = [];
                            crud.table.ajax.reload();
                        },
                        error: function(result) {
                            new Noty({
                                type: "danger",
                                text: "<strong>Status update failed</strong><br>Please try again."
                            }).show();
                        }
                    });
                }
            });
        }
    }
</script>
@endpush

Closing, please re-open or comment if you are still experiencing issues.

Cheers