5

I've created a grid via ui component with a mass action. I've also added a selections column so when the user click on one or more checkboxes, the items in the grid get selected and the mass action can be called on those items.

This work if the user selects only some records from the list, but if the user selects all the items by the action dropdown "select all", then the mass action cannot work.

This is because different parameters are used: - when the user checks the boxes individually, then the "selected" parameter is passed to my controller with the id selected - when the user chooses "select all", no "selected" parameter is passed. Instead, there is a parameter "excluded" false and no ids are passed to my controller.

Magento doesn't need the ids when "select all" is chosen because the mass action simply loads the collection. My problem is that my grid is not related to a table, so I cannot call any model/collection. I have a custom data provider that gets the data from an external source and displays the data in the grid.

Calling the external data all over again in the mass action controller is far more expensive than simply passing the ids, so simulating the magento way of reloading the collection is not a desirable option. The best thing in my opinion would be to pass the "selected" parameter also when all items are selected with the "select all" dropdown option.

Is there a way to achive this?

My code is pretty standard:

  • the mass action and the grid is build via ui component listing:
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
...
<listingToolbar name="listing_top">
  <massaction name="listing_massaction">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="selectProvider" xsi:type="string">my_provider.ids</item>
                    <item name="indexField" xsi:type="string">id</item>
                </item>
            </argument>
            <action name="massaction_name">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="type" xsi:type="string">massaction_name</item>
                        <item name="label" xsi:type="string" translate="true">Mass Action Name</item>
                        <item name="url" xsi:type="url" path="route/controller/path"/>
                        <item name="confirm" xsi:type="array">
                            <item name="title" xsi:type="string" translate="true">Title</item>
                            <item name="message" xsi:type="string" translate="true">Are you sure?</item>
                        </item>
                    </item>
                </argument>
            </action>
        </massaction>
...
<columns name="my_provider_columns">
   <column name="ids">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">id</item>
                    <item name="dataType" xsi:type="string">select</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/onoff</item>
                </item>
            </argument>
        </column>
...
  • the controller retrieves the request parameters:
...
class Path extends Action implements HttpGetActionInterface, HttpPostActionInterface
...
public function execute() {
  $params = $this->getRequest()->getParams();
}
...

As I mentioned, $params returns an array "selected" with the ids or a flag set to false if "selectAll" was used for the selection:

array (
  'selected' => 
  array (
    0 => '505928',
...
array (
  'excluded' => 'false'
...

But I need the "selected" array also when the selectAll option is used. Is there a way to get it?

5
  • Were you able to solve this issue?
    – Eric Libay
    Commented Jun 4, 2020 at 22:14
  • @Lou Nik did you get the solution for this? Commented Mar 25, 2021 at 11:16
  • @jafarpinjar hi, I haven't been able to find a real solution, sorry, what I did was to remove the "select all" option from the dropdown and add a separate button. This way I was able to pass as a parameter the selected ids. If your grid hasn't many records, a simpler solution would be, I think, to simply reload the results, simulating a collection
    – Lou Nik
    Commented Mar 26, 2021 at 14:48
  • any soluction guys facing same issue
    – Devidas
    Commented Feb 24, 2023 at 8:43
  • please help me on this
    – Devidas
    Commented Feb 24, 2023 at 8:43

6 Answers 6

2

You were so close! On Magento 2 grids

Mass Action Can Work Two Ways.

For smaller collections, I find this works find

protected function massAction(AbstractCollection $collection) {

$invoiceIds = $this->getRequest()->getParam('selected');

For larger collections, it fires the Execute function. This example is from the invoice grid

public function execute()
    {

        $collection = $this->filter->getCollection($this->invoicecollectionFactory->create());
3
  • hello @elfling, what is the solution for this issue, I am also facing the same problem Commented Mar 25, 2021 at 11:15
  • You have to make sure you have both the execute and the massAction functions in the controller
    – elfling
    Commented Jun 2, 2021 at 13:11
  • how to hide the grid based on condition like module active or not? Commented Jun 7, 2021 at 16:42
1

Use a custom JS AMD module that extends from Magento_Ui/js/grid/tree-massactions.

You will configure the mass listing to use this module on this node here <massaction name="listing_massaction" component="Vendor_Module/path/to/js/module">

In the custom JS override the following method from the parent

defaultCallback: function (action, data) {

Change

var itemsType = data.excludeMode ? 'excluded' : 'selected',

With

var itemsType = 'selected',

Keep everything else from the parent method.

Clean cache. Redeploy static-content if needed.

There is a drawback though, I'm almost sure this will send you only the ids from the current page, I say this for obvious reasons the markup for the other pages is not yet loaded so the checkboxes can not be checked. I suspect that is the point of the 'excluded' feature as well.

If you do not know how to extend Magento_Ui/js/grid/tree-massactions take a look here Use custom JS official docs

1
  • Yea, you are right, it's only send the ids from the current page, which is not the correct answer we're looking for.
    – fudu
    Commented May 6, 2021 at 8:36
0

Update your listing file

    <massaction name="listing_massaction" component="Magento_Ui/js/grid/tree-massactions">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="selectProvider" xsi:type="string">your_custom_listing.your_custom_listing.my_provider_columns.ids</item>
                <item name="component" xsi:type="string">Magento_Ui/js/grid/tree-massactions</item>
                <item name="indexField" xsi:type="string">id</item>
            </item>
        </argument>
        <action name="is_delete">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="type" xsi:type="string">delete</item>
                    <item name="label" xsi:type="string" translate="true">Delete</item>
                    <item name="url" xsi:type="url" path="your_admin_route_name/your_admin_contoller_name/massDelete">
                    </item>
                    <item name="confirm" xsi:type="array">
                        <item name="title" xsi:type="string" translate="true">Delete items</item>
                        <item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected items?</item>
                    </item>
                </item>
            </argument>
        </action>
    </massaction>

And your mass delete file is here

<?php

namespace VendoreName\ModuleName\Controller\Adminhtml\YourContollerName;

use VendoreName\ModuleName\Model\ResourceModel\YourModelName\CollectionFactory;
use VendoreName\ModuleName\Model\YourModelNameFactory;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;

class MassDelete extends \Magento\Backend\App\Action
{
    protected $filter;
    protected $collectionFactory;
    protected $CustomeModel;

    public function __construct(
        Context $context,
        Filter $filter,
        CollectionFactory $collectionFactory,
        YourModelNameFactory $CustomeModel
    ) {
        $this->filter = $filter;
        $this->collectionFactory = $collectionFactory;
        $this->CustomeModel = $CustomeModel;
        parent::__construct($context);
    }

    public function execute()
    {
        $CustomData = $this->collectionFactory->create();
        foreach ($CustomData as $value) {
            $templateId[] = $value['id'];
        }
        $parameterData = $this->getRequest()->getParams('id');
        $selectedAppsid = $this->getRequest()->getParams('id');
        if (array_key_exists("selected", $parameterData)) {
            $selectedAppsid = $parameterData['selected'];
        }
        if (array_key_exists("excluded", $parameterData)) {
            if ($parameterData['excluded'] == 'false') {
                $selectedAppsid = $templateId;
            } else {
                $selectedAppsid = array_diff($templateId, $parameterData['excluded']);
            }
        }
        $collection = $this->collectionFactory->create();
        $collection->addFieldToFilter('id', ['in' => $selectedAppsid]);
        $delete = 0;
        $model = [];
        foreach ($collection as $item) {
            $this->deleteById($item->getId());
            $delete++;
        }
        $this->messageManager->addSuccess(__('A total of %1 Records have been deleted.', $delete));
        $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
        return $resultRedirect->setPath('*/*/');
    }

    public function deleteById($id)
    {
        $item = $this->CustomeModel->load($id);
        $item->delete();
    }
}

I Hope This Helps You.

0

Can you refer code used in following blog..See approved solution.

Admin Grid "Select All" for mass action only sends the visible IDs

In above link there is code for select All for mass action.

1
  • Maybe you are misunderstanding the request, he say no "selected" parameter is passed. Instead, there is a parameter "excluded" false and no ids are passed to my controller. But your provided link is about "selected" parameter is only from the current page.
    – fudu
    Commented May 6, 2021 at 8:20
0

After 2 days debug about this, i've found that this is the feature of Magento.
It won't post the "selected" param to the controller.
Instead, we should get the ids by this way:

$collection = $this->filter->getCollection($this->collectionFactory->create());
$productIds = $collection->getAllIds();

In above code, $this->filter->getCollection()->getAllids() is Magento way to get all the pushed ids.

Note: The $this->filter is get from Magento\Ui\Component\MassAction\Filter, and $this->collectionFactory is get from your custom collection,
in this case is Magento\Catalog\Model\ResourceModel\Product\CollectionFactory

You can check more information and example in
vendor/magento/module-catalog/Controller/Adminhtml/Product/MassStatus.php

Example Full Code:

<?php

use Magento\Ui\Component\MassAction\Filter;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;

class MassStatus
{

    public function __construct(
        \Magento\Backend\App\Action\Context $context
        Filter $filter,
        CollectionFactory $collectionFactory,
    ) {
        $this->filter = $filter;
        $this->collectionFactory = $collectionFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        $collection = $this->filter->getCollection($this->collectionFactory->create());
        $productIds = $collection->getAllIds();
        // do something ..
    }
}

Hope it's help.

3
  • Hi, I agree, that is magento standard way. Unfortunately, magento standard way doesn't contemplate the case where you need a grid but no table. If you read my post, I clearly wrote: "my grid is not related to a table, so I cannot call any model/collection. My custom data provider gets the data from an external source", so calling the external source is very expensive; if I could have gotten the ids directly from the grid, I could have skipped it and saved resources. So getting the ids from the grid and NOT from the collection was the essential part of the post
    – Lou Nik
    Commented May 7, 2021 at 8:57
  • I see, so this is a really hard question indeed. My answer are not helpful for your question but i thing it's still useful for some one who have troubles with the default grid of Magento.
    – fudu
    Commented May 10, 2021 at 1:53
  • yes, I agree, it is helpful and it is always a good thing to follow the standard way whenever possible. I wish there were a standard way for my case too but maybe Magento didn't think somebody could need it or maybe it is intended behaviour that ui_component should be used only with CRUD operation, I haven't found a satisfying answer yet
    – Lou Nik
    Commented May 10, 2021 at 13:47
-1

Use a custom JS AMD Module to extend (recommended) or directly update core file (not recommended)

Path: vendor/magento/module-ui/view/base/web/js/grid/massactions.js

change defaultCallback function

From:

defaultCallback: function (action, data) {
        var itemsType = data.excludeMode ? 'excluded' : 'selected',
            selections = {};

        selections[itemsType] = data[itemsType];

        if (!selections[itemsType].length) {
            selections[itemsType] = false;
        }

        _.extend(selections, data.params || {});

        utils.submit({
            url: action.url,
            data: selections
        });
    },

To:

defaultCallback: function (action, data) {
        var itemsType = data.excludeMode ? 'excluded' : 'selected',
            selections = data;

        selections[itemsType] = data[itemsType];

        if (!selections[itemsType].length) {
            selections[itemsType] = false;
        }

        _.extend(selections, data.params || {});

        utils.submit({
            url: action.url,
            data: selections
        });
    },

In short only change selections = {}; to selections = data; Thats all!

1
  • not working....
    – Devidas
    Commented Feb 24, 2023 at 8:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.