October: Update backend UI elements after list filter is applied

Created on 7 Jun 2017  路  7Comments  路  Source: octobercms/october

When using a scoreboard on top of a list, the scoreboard data cannot be updated to reflect the changes in the list when a filter is applied.

A good example is a list of invoices and a total sum in the scoreboard. When you filter the invoices by the client, the sum in the scoreboard should be calculated based on the currently visible invoices.

I figure there are 2 solutions to this issue:

  1. Fire an event when the list filter is applied. Then a listener can be added to return, for example, a partial with the scoreboard.
  2. Add a function to the ListController.php that is called when the filter is applied.

Currently I implemented the second solution to be used in my plugin. I can make PR with the changes.

But the question is, which logic is preferrable to the overall logic of the CMS?

Low Archived Conceptual Enhancement

Most helpful comment

@bogandix @robinbonnes I have a temporary solution to refresh partials after a list filter change.
I added the following code to my controller to update the total amount of items for the current filter:

   /**
     * @var bool
     */
    private $extendFilterOnce = false;

    /**
     * @param Filter $filter
     * @return void
     */
    public function listFilterExtendScopes(Filter $filter): void
    {
        if ($this->extendFilterOnce) {
            return;
        }

        $this->extendFilterOnce = true;

        $filter->onFilterUpdate();

        $query = YourModel::query();

        foreach ($filter->getScopes() as $scope) {
            /** @noinspection PhpParamsInspection */
            $filter->applyScopeToQuery($scope, $query);
        }

        $filter->bindEvent('filter.update', function () use ($query): array {
            return [
                '#scoreboard-total' => $query->count(),
            ];
        });
    }

All 7 comments

See https://github.com/octobercms/october/pull/2866 for how this is usually handled. Check the modules/backend/widgets/filter to see if there are any existing events that you could hook into.

Closing as it has been over a month since any activity on this occurred.

Same issue here. Want to update some values on the scoreboard above the backend list, based on the selected filters. But when I apply a filter it only refreshes the list. Refreshing the whole page would be enough. Maybe some event is a good solution?

Tried the following in boot() function from plugin.php file:

        Event::listen('filter.update', function($params) {
            // Refresh current controller from here if possible?
        });

Also checking the applied filters is a little bit hard, for now I use this ugly function in the controller to get the filters:

    function getCurrentFilters() {
        $filters = [];
        foreach (\Session::get('widget', []) as $name => $item) {
            if (str_contains($name, 'Filter')) {
                $filter = @unserialize(@base64_decode($item));
                if ($filter) {
                    array_push($filters, $filter);
                }
            }
        }

        return $filters;
    }

Then I loop through the filters in the controllers custom index() function and change the sql query to get the appropriate results for the scoreboard:

            $filters = $this->getCurrentFilters();
            $categories = [];

            if(count($filters) > 0)
            {
                foreach($filters as $filter) {
                    if(isset($filter['scope-category']) && count($filter['scope-category']) > 0)
                    {
                        $categories += array_keys($filter['scope-category']);
                    }
                }
            }

            if(count($categories) > 0)
            {
                $this->vars['total'] = Record::whereIn('category_id', $categories)->sum('price');
            }
            else
            {
                $this->vars['total'] = Record::sum('price');
            }

Any solution?

@robinbonnes the relation controller has a method used to extend partial results, perhaps we could implement something similar to that with the list controller

Same issue here. Want to update some values on the scoreboard above the backend list, based on the selected filters. But when I apply a filter it only refreshes the list. Refreshing the whole page would be enough. Maybe some event is a good solution?

Tried the following in boot() function from plugin.php file:

        Event::listen('filter.update', function($params) {
            // Refresh current controller from here if possible?
        });

Also checking the applied filters is a little bit hard, for now I use this ugly function in the controller to get the filters:

    function getCurrentFilters() {
        $filters = [];
        foreach (\Session::get('widget', []) as $name => $item) {
            if (str_contains($name, 'Filter')) {
                $filter = @unserialize(@base64_decode($item));
                if ($filter) {
                    array_push($filters, $filter);
                }
            }
        }

        return $filters;
    }

Then I loop through the filters in the controllers custom index() function and change the sql query to get the appropriate results for the scoreboard:

            $filters = $this->getCurrentFilters();
            $categories = [];

            if(count($filters) > 0)
            {
                foreach($filters as $filter) {
                    if(isset($filter['scope-category']) && count($filter['scope-category']) > 0)
                    {
                        $categories += array_keys($filter['scope-category']);
                    }
                }
            }

            if(count($categories) > 0)
            {
                $this->vars['total'] = Record::whereIn('category_id', $categories)->sum('price');
            }
            else
            {
                $this->vars['total'] = Record::sum('price');
            }

Any solution?

you can use the widget list query to get the condtions
$this->widget->list->prepareQuery() which returns the query builder instance; there is a "where" with the filtered conditions

@bogandix @robinbonnes I have a temporary solution to refresh partials after a list filter change.
I added the following code to my controller to update the total amount of items for the current filter:

   /**
     * @var bool
     */
    private $extendFilterOnce = false;

    /**
     * @param Filter $filter
     * @return void
     */
    public function listFilterExtendScopes(Filter $filter): void
    {
        if ($this->extendFilterOnce) {
            return;
        }

        $this->extendFilterOnce = true;

        $filter->onFilterUpdate();

        $query = YourModel::query();

        foreach ($filter->getScopes() as $scope) {
            /** @noinspection PhpParamsInspection */
            $filter->applyScopeToQuery($scope, $query);
        }

        $filter->bindEvent('filter.update', function () use ($query): array {
            return [
                '#scoreboard-total' => $query->count(),
            ];
        });
    }

This issue will be closed and archived in 3 days, as there has been no activity in the last 30 days. If this issue is still relevant or you would like to see it actioned, please respond and we will re-open this issue.

Was this page helpful?
0 / 5 - 0 ratings