In the previous part of this article, we created the base XML file with a UI Component to present the grid view in the backend. In this part, we will enhance this XML with additional functionalities.
1. Mass action
As you can see in the Magento admin panel, some grids have implemented actions which can be executed on more than one item selected with the checkboxes. These are mass actions. They are very useful, so let’s try to add them to our grid.
First, we’ll add an extra column of checkboxes assigned to every item. In order to do this, find <columns> tag and as a first column add <selectionsColumn>
// file: Magently/MyUiComponent/view/adminhtml/ui_component/myproducts_listing.xml
<listing>
<!-- ... other block of code -->
<columns name="myproducts_columns">
<selectionsColumn name="ids" sortOrder="5">
<settings>
<indexField>id</indexField>
<label translate="true">Select</label>
</settings>
</selectionsColumn>
<column name="id" sortOrder="10">
<!-- ... other block of code -->
Note, that for <selectionsColumn> we set sortOrder to 5, so it is displayed as a first column before the ID column, which has sortOrder = 10 and we named it “ids”. In the settings, we need to set indexField as “id” – it’s a primary key of our table. That’s all here. After clearing the cache and removing the UI bookmark from the database, a new column with checkboxes will be presented.
But there is more! The selector is still missing and a mass action can’t be chosen. In order to do this, find <listingToolbar> tag and once there, add extra <massaction> block:
// file: Magently/MyUiComponent/view/adminhtml/ui_component/myproducts_listing.xml
<listing>
<!-- ... other block of code -->
<listingToolbar name="listing_top">
<!-- ... other block of code →
<massaction name="listing_massaction">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">
myproducts_listing.myproducts_listing.myproducts_columns.ids
</item>
<item name="indexField" xsi:type="string">id</item>
</item>
</argument>
<action name="delete">
<settings>
<confirm>
<message translate="true">
Are you sure you want to delete selected product?
</message>
<title translate="true">Delete Selected Products</title>
</confirm>
<url path="*/*/massDelete"/>
<type>delete</type>
<label translate="true">Delete Selected Products</label>
</settings>
</action>
</massaction>
</listingToolbar>
<!-- ... other block of code -->
</listing>
In the new <massaction> tag, first we need to set “selectProvider” which indicates our <selectionsColumn> (“ids” name). We also need to set “indexField” as ID of our database table.
Now, we have to add an action called “delete”. In the action’s settings, we define a confirmation message and URL to the controller which we have to create, to handle this action. Next, we set the type of action to “delete” and label it for a mass action selector. This concludes the changes in UI Component XML file.
Let’s see how to handle a mass action inside a controller. To do this, we have to create a new MassDelete.php file in the Controller\Adminhtml\Index location.
// file: Magently/MyUiComponent/Controller/Adminhtml/Index/MassDelete.php
<?php
namespace Magently\MyUiComponent\Controller\Adminhtml\Index;
use Magento\Framework\Controller\ResultFactory;
class MassDelete extends \Magento\Backend\App\Action
{
/**
* @var \Magento\Ui\Component\MassAction\Filter
*/
private $filter;
/**
* @var \Magently\MyUiComponent\Model\ResourceModel\MyProducts\CollectionFactory
*/
private $collectionFactory;
/**
* @var \Magently\MyUiComponent\Api\MyProductsRepositoryInterface
*/
private $productRepository;
/**
* Constructor
*
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Ui\Component\MassAction\Filter $filter
* @param \Magently\MyUiComponent\Model\ResourceModel\MyProducts\CollectionFactory $collectionFactory
* @param \Magently\MyUiComponent\Api\MyProductsRepositoryInterface $productRepository
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Ui\Component\MassAction\Filter $filter,
\Magently\MyUiComponent\Model\ResourceModel\MyProducts\CollectionFactory $collectionFactory,
\Magently\MyUiComponent\Api\MyProductsRepositoryInterface $productRepository
) {
parent::__construct($context);
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
$this->productRepository = $productRepository;
}
/**
* Execute the action.
*
* @return \Magento\Framework\Controller\ResultInterface
* @throws \Magento\Framework\Exception\NotFoundException
*/
public function execute()
{
if (!$this->getRequest()->isPost()) {
throw new \Magento\Framework\Exception\NotFoundException(__('Page not found'));
}
$collection = $this->filter->getCollection($this->collectionFactory->create());
/** @var \Magento\Catalog\Model\Product $product */
foreach ($collection->getItems() as $product) {
$this->productRepository->delete($product);
}
$this->messageManager->addSuccessMessage(
__('A total of %1 record(s) have been deleted.', $collection->getSize())
);
return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('*/*/index');
}
}
In the similar way, you can add more actions by adding a next <action> tag and preparing another controller. Now, you can select many items in the grid and delete them all at once.
2. Actions column
As you can see, in the Magento backend, the last column of many grid views is “Action”. This column often presents action links like “Edit”, “View”, “Delete” etc. Let’s add this column to our grid view.
Find <columns> tag and at the end add a special <actionsColumn> tag:
// file: Magently/MyUiComponent/view/adminhtml/ui_component/myproducts_listing.xml
<listing>
<!-- ... other block of code -->
<columns name="myproducts_columns">
<!-- ... other block of code -->
<actionsColumn
name="actions"
class="Magently\MyUiComponent\Ui\Component\Listing\Columns\BlockActions">
<settings>
<indexField>id</indexField>
</settings>
</actionsColumn>
</columns>
</listing>
We have just added an actions column with a class argument. This class needs to be added to our module, where we design what will be rendered in the actions column. In <settings>, we set <indexField> value to the primary ID column name of our database.
Let’s get back to the BlockActions class. The idea is similar to rendering images (from part 2 of this article “link”), so we have to extend the Magento\Ui\Component\Listing\Columns\Column class and override the prepareDataSource function:
// file: Magently/MyUiComponent/Ui/Component/Listing/Columns/BlockActions.php
<?php
namespace Magently\MyUiComponent\Ui\Component\Listing\Columns;
/**
* Class for displaying actions column in UI Component Grid view
*/
class BlockActions extends \Magento\Ui\Component\Listing\Columns\Column
{
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {
if (isset($item['id'])) {
$item[$this->getData('name')] = [
'edit' => [
'href' => $this->getContext()->getUrl(
'*/*/edit',
[
'id' => $item['id']
]
),
'label' => __('Edit')
],
'delete' => [
'href' => $this->getContext()->getUrl(
'*/*/delete',
[
'id' => $item['id']
]
),
'label' => __('Delete'),
'confirm' => [
'title' => __('Delete'),
'message' => __('Are you sure you want to delete this item?')
]
]
];
}
}
}
return $dataSource;
}
}
As you can see above, we added two actions with links: “edit” and “delete”. Now, you have to add controllers to handle these. Additionally, we added a confirmation message to the delete action.
3. Adding an “on click” action to a whole item
If you need to add the click action to a whole row in the grid in order to call some action like “edit”, you have to add extra settings to <columns>:
// file: Magently/MyUiComponent/view/adminhtml/ui_component/myproducts_listing.xml
<listing>
<!-- ... other block of code -->
<columns name="myproducts_columns">
<settings>
<childDefaults>
<param name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">
myproducts_listing.myproducts_listing.myproducts_columns.actions
</item>
<item name="target" xsi:type="string">applyAction</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">edit</item>
<item name="1" xsi:type="string">${ $.$data.rowIndex }</item>
</item>
</param>
</childDefaults>
</settings>
<!-- ... other block of code -->
</columns>
</listing>
We need to add a “fieldAction” array that contains a “provider” item that points at our actions column and a “target” item set to “applyAction”. We also need an additional “params” array with the action name. In our case it is “edit”. This name must be the same as we defined in Magently/MyUiComponent/Ui/Component/Listing/Columns/BlockActions.php file for actions column in the previous step.
4. Inline edit row
Another way of editing a single item is by using inline edit without leaving the grid view. Thanks to a UI Component it is quite simple to implement. To do this, we have to add extra settings into <columns>:
// file: Magently/MyUiComponent/view/adminhtml/ui_component/myproducts_listing.xml
<listing>
<!-- ... other block of code -->
<columns name="myproducts_columns">
<settings>
<editorConfig>
<param name="clientConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="*/*/inlineEdit"/>
<item name="validateBeforeSave" xsi:type="boolean">false</item>
</param>
<param name="indexField" xsi:type="string">id</param>
<param name="enabled" xsi:type="boolean">true</param>
<param name="selectProvider" xsi:type="string">
myproducts_listing.myproducts_listing.myproducts_columns.ids
</param>
</editorConfig>
<childDefaults>
<param name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">
myproducts_listing.myproducts_listing.myproducts_columns_editor
</item>
<item name="target" xsi:type="string">startEdit</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
<item name="1" xsi:type="boolean">true</item>
</item>
</param>
</childDefaults>
</settings>
<!-- ... other block of code -->
</columns>
</listing>
In columns settings, we have to put some values for “editorConfig”. First, let’s add items to the “clientConfig” array. The “saveUrl” item is the URL of our controller which we will create in a moment. Next parameter is “indexField” with “id” value as a primary key of our table in the database. Then, we have to set “enabled” tag to “true”. This is easy way to enable/disable the inline editor functionality. If we set its value to “false”, it will disable the inline editor.
Moreover, we have the “selectProvider” parameter, its value set to “ids”, which is a name of the first column defined by the <selectionsColumn> tag.
Next, like in the previous step, we have to set items for the “fieldAction” array. This means that we have to decide what functionality do we want:
1) an onclick action redirecting to the edit item view, as described in the step 3 of this article
2) inline edition directly in the grid view, as described in this step
Obviously, we have to choose only one option, we cannot have both 🙂
For the “fieldAction” array, first, we add a “provider” item with a specific value. Generally, it’s set to <columns> tag name but with addition of “_editor” suffix. Next, we have the “target” item with the “startEdit” value and extra “params” items.
But it’s not all. Now, we have to select which fields will be available for inline editing by indicating specific type of data for a particular column. So let’s enable the “name” column for editing. For this, find a <column> tag with its name argument set to “name” and add the <editor> tag like below:
// file: Magently/MyUiComponent/view/adminhtml/ui_component/myproducts_listing.xml
<listing>
<!-- ... other block of code -->
<columns name="myproducts_columns">
<!-- ... other block of code -->
<column name="name" sortOrder="20">
<settings>
<filter>text</filter>
<label translate="true">Name</label>
<editor>
<editorType>text</editorType>
</editor>
</settings>
</column>
<!-- ... other block of code -->
</columns>
</listing>
So, we decided that we want to edit this column and its “text” type of data. Thanks to this, Magento will render the input text for this field. Obviously, for different type of data we have to use different types. We also have “date” and “select”, where data will be rendered: for date, date time picker and for select, the select field with all items which we defined in provider.
If a field needs some additional validation, we add it in the <validation> tag inside <editor> block.
// file: Magently/MyUiComponent/view/adminhtml/ui_component/myproducts_listing.xml
<listing>
<!-- ... other block of code -->
<columns name="myproducts_columns">
<!-- ... other block of code -->
<column name="name" sortOrder="20">
<settings>
<filter>text</filter>
<label translate="true">Name</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
</editor>
</settings>
</column>
<!-- ... other block of code -->
</columns>
</listing>
So now the “name” field is required and must be filled.
Finally, we need to add our controller class that supports the action of saving the edited row:
// file: Magently/MyUiComponent/Controller/Adminhtml/Index/InlineEdit.php
class InlineEdit extends \Magento\Backend\App\Action
{
public function execute()
{
if ($this->getRequest()->getParam('isAjax')) {
$postItems = $this->getRequest()->getParam('items', []);
// save our data here
}
}
}
This is an AJAX action, so first we have to check that the request has “isAjax” parameter. Next, we have a getter of the “items” parameter which returns the array with data of the edited item. So we can save the data by our model repository.
5. Set default filter
Sometimes, we want to have some filters enabled by default. For our example, we set default filter with “is_valid” field set to “yes”. To do this, add an extra block directly after the <filters> tag:
// file: Magently/MyUiComponent/view/adminhtml/ui_component/myproducts_listing.xml
<listing>
<!-- ... other block of code -->
<listingToolbar name="listing_top">
<!-- ... other block of code -->
<filters name="listing_filters">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="applied" xsi:type="array">
<item name="is_valid" xsi:type="string">1</item>
<!-- add more items here -->
</item>
</item>
</argument>
<!-- ... other block of code -->
</filters>
</listingToolbar>
<!-- ... other block of code -->
</listing>
So for the “applied” array, we add our own item, with a proper name of the field (in our case it’s “is_valid”) and the value 1 for example. Of course, you can add more items with another set of fields and default values.
6. Summary
This concludes the third and final part of this article. I hope it helps you creating a list view and adding useful functions. Feel free to write your questions and suggestions in the comments!