Laravel-Backpack / community-forum

A workspace to discuss improvement and feature ideas, before they're actually implemented.
28 stars 0 forks source link

AJAX Widgets / refreshable / smth #126

Open tabacitu opened 2 years ago

tabacitu commented 2 years ago

When widgets are used on CRUD pages, it's annoying that they don't update along with the DataTable, as you filter/search/etc. For example in https://github.com/Laravel-Backpack/CRUD/issues/2200

If you use Widgets there... as a user... you expect them to update. And it's not very easy to implement as a Dev. You have to refresh the page with all GET parameters in order to force the recalculation of the widgets.

Not sure what solution we should implement for this, but I do think we should think of one.

dimer47 commented 9 months ago

Hi @tabacitu

According to @pxpm's proposal in the issue https://github.com/Laravel-Backpack/CRUD/issues/2200. I modified the code of datatables_logic.blade.php to add the widget update in the datatable draw callback.

This is a workaround, because it will not work for widgets used javascript load on before or after scripts (e.g. charts). Also, previous JS instances are not destroyed and cleaned from the browser memory.

To propose a viable and effective solution, we should, in my opinion, review the functioning of widgets so that they load their data through independent API routes. What do you think of this idea?

I put the modified code below to help developers like me who need to find a stopgap solution (for widgets without JS) while waiting for an evolution.

datatables_logic.blade.php

Change :

$('#crudTable').on('draw.dt', function () {
            crud.functionsToRunOnDataTablesDrawEvent.forEach(function (functionName) {
                crud.executeFunctionByName(functionName);
            });
            if ($('#crudTable').data('has-line-buttons-as-dropdown')) {
                formatActionColumnAsDropdown();
            }
        }).dataTable();

With :

let firstUrl = window.location.href;

$('#crudTable').on('draw.dt', function () {
            let currentUrl = window.location.href;
            let widgets = $('div[data-element="widget"]');

            if (firstUrl !== currentUrl) {
                if (widgets.length > 0) {
                    widgets.fadeTo(200, 0.20);

                    $.get(currentUrl, function (data) {
                        let newWidgets = $(data).find('div[data-element="widget"]');

                        newWidgets.each(function () {
                            let widgetSection = $(this).attr('section');
                            let widgetContent = $(this).html();

                            $('div[section="' + widgetSection + '"]').html(widgetContent);
                        });

                        widgets.fadeTo(200, 1);
                    });
                }
            }

            crud.functionsToRunOnDataTablesDrawEvent.forEach(function (functionName) {
                crud.executeFunctionByName(functionName);
            });
            if ($('#crudTable').data('has-line-buttons-as-dropdown')) {
                formatActionColumnAsDropdown();
            }
        }).dataTable();

views/ui/widgets/inc/wrapper_start.blade.php

Change :

@php
    $widget['wrapper']['element'] = $widget['wrapper']['element'] ?? 'div';
    $widget['wrapper']['class'] = $widget['wrapper']['class'] ?? "col-sm-6 col-md-4";

    // each wrapper attribute can be a callback or a string
    // for those that are callbacks, run the callbacks to get the final string to use
    foreach($widget['wrapper'] as $attribute => $value) {
        $widget['wrapper'][$attribute] = (!is_string($value) && is_callable($value) ? $value() : $value) ?? '';
    }
@endphp

With :

@php
    $widget['wrapper']['element'] = $widget['wrapper']['element'] ?? 'div';
    $widget['wrapper']['class'] = $widget['wrapper']['class'] ?? "col-sm-6 col-md-4";
    $widget['wrapper']['data-element'] = "widget";

    // each wrapper attribute can be a callback or a string
    // for those that are callbacks, run the callbacks to get the final string to use
    foreach($widget['wrapper'] as $attribute => $value) {
        $widget['wrapper'][$attribute] = (!is_string($value) && is_callable($value) ? $value() : $value) ?? '';
    }
@endphp

cc @oddvalue