2.02

Admin Grid/Form Components

Learn how to create and modify admin grids and forms using UI Components to manage custom entities in the Magento admin panel.

Why This Matters: Understanding how to create and modify admin grids and forms is essential for building custom admin interfaces in Magento. This knowledge allows you to manage custom entities, display data, and provide CRUD operations in the admin panel.

Admin Grid/Form Components Overview

mindmap root((Admin UI)) Admin Grids Controller Layout XML UI Component XML DataSource Mass Actions Inline Editing Admin Forms Controllers CRUD Layout XML UI Component XML DataSource Field Components Components UiComponent Listing Form DataProvider Collection

Admin Grids Overview

A standard admin grid displays data from a database table in a tabular format with features like sorting, filtering, pagination, and mass actions.

Key Assumption: Standard admin grids assume there is an underlying table and collection that allows fetching data from the table.

Steps to Create an Admin Grid

Required Steps

  1. Create a new controller in adminhtml
  2. Modify layout to include the UiComponent's configuration
  3. Create a listing (grid) component XML file
  4. Create/configure the DataSource for the grid
  5. Create controllers for mass actions
  6. Optionally - Create a controller for inline editing

Step 1: Create Admin Controller

Controller/Adminhtml/Entity/Index.php
<?php
namespace Vendor\Module\Controller\Adminhtml\Entity;

use Magento\Backend\App\Action;
use Magento\Framework\View\Result\PageFactory;

class Index extends Action
{
    const ADMIN_RESOURCE = 'Vendor_Module::entity';
    
    protected $resultPageFactory;
    
    public function __construct(
        Action\Context $context,
        PageFactory $resultPageFactory
    ) {
        parent::__construct($context);
        $this->resultPageFactory = $resultPageFactory;
    }
    
    public function execute()
    {
        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('Vendor_Module::entity');
        $resultPage->getConfig()->getTitle()->prepend(__('Manage Entities'));
        return $resultPage;
    }
}

Step 2: Layout XML Declaration

view/adminhtml/layout/entity_index.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="styles"/>
    <body>
        <referenceContainer name="content">
            <uiComponent name="entity_listing"/>
        </referenceContainer>
    </body>
</page>
Key Element: The <uiComponent name="entity_listing"/> references the UI Component XML file.

Step 3: Listing (Grid) Component XML

view/adminhtml/ui_component/entity_listing.xml
<?xml version="1.0"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">
                entity_listing.entity_listing_data_source
            </item>
        </item>
    </argument>
    
    <dataSource name="entity_listing_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">
                Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider
            </argument>
            <argument name="name" xsi:type="string">entity_listing_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
            <argument name="requestFieldName" xsi:type="string">id</argument>
        </argument>
    </dataSource>
    
    <columns name="entity_columns">
        <column name="entity_id">
            <settings>
                <label translate="true">ID</label>
            </settings>
        </column>
        <column name="title">
            <settings>
                <filter>text</filter>
                <label translate="true">Title</label>
            </settings>
        </column>
    </columns>
</listing>

Step 4: DataSource Configuration (di.xml)

etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    
    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="entity_listing_data_source" xsi:type="string">
                    Vendor\Module\Model\ResourceModel\Entity\Grid\Collection
                </item>
            </argument>
        </arguments>
    </type>
    
    <type name="Vendor\Module\Model\ResourceModel\Entity\Grid\Collection">
        <arguments>
            <argument name="mainTable" xsi:type="string">entity_table</argument>
            <argument name="eventPrefix" xsi:type="string">entity_grid_collection</argument>
            <argument name="eventObject" xsi:type="string">entity_grid_collection</argument>
            <argument name="resourceModel" xsi:type="string">
                Vendor\Module\Model\ResourceModel\Entity
            </argument>
        </arguments>
    </type>
</config>
⚠️ Important: The collection name in di.xml must match the dataSource name in the UI Component XML.

Grid Collection with SearchResultInterface

A separate Grid Collection is created to implement SearchResultInterface. You may use the standard collection as well if implementing this interface.

<?php
namespace Vendor\Module\Model\ResourceModel\Entity\Grid;

use Magento\Framework\Api\Search\SearchResultInterface;
use Vendor\Module\Model\ResourceModel\Entity\Collection as EntityCollection;

class Collection extends EntityCollection implements SearchResultInterface
{
    protected $aggregations;
    
    public function getAggregations() { return $this->aggregations; }
    public function setAggregations($aggregations) { $this->aggregations = $aggregations; }
    public function getSearchCriteria() { return null; }
    public function setSearchCriteria(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria = null) { return $this; }
    public function getTotalCount() { return $this->getSize(); }
    public function setTotalCount($totalCount) { return $this; }
    public function setItems(array $items = null) { return $this; }
}
Why Grid Collection? The main reason for having a separate Grid Collection is to implement SearchResultInterface.

Mass Actions

Mass actions allow users to perform operations on multiple grid items at once.

Configuring Mass Actions

<listingToolbar name="listing_top">
    <massaction name="listing_massaction">
        <action name="delete">
            <settings>
                <confirm>
                    <message translate="true">Are you sure you want to delete selected items?</message>
                    <title translate="true">Delete items</title>
                </confirm>
                <url path="*/*/massDelete"/>
                <type>delete</type>
                <label translate="true">Delete</label>
            </settings>
        </action>
        
        <action name="disable">
            <settings>
                <url path="*/*/massDisable"/>
                <type>disable</type>
                <label translate="true">Disable</label>
            </settings>
        </action>
        
        <action name="enable">
            <settings>
                <url path="*/*/massEnable"/>
                <type>enable</type>
                <label translate="true">Enable</label>
            </settings>
        </action>
        
        <!-- Edit Action (triggers JS module) -->
        <action name="edit">
            <settings>
                <callback>
                    <target>editSelected</target>
                    <provider>entity_listing.entity_listing.entity_columns_editor</provider>
                </callback>
                <type>edit</type>
                <label translate="true">Edit</label>
            </settings>
        </action>
    </massaction>
</listingToolbar>
⚠️ Note: Some mass actions (like edit) may trigger a JavaScript module rather than issuing an immediate request.

Mass Action Controller Example

<?php
namespace Vendor\Module\Controller\Adminhtml\Entity;

use Magento\Backend\App\Action;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;
use Vendor\Module\Model\ResourceModel\Entity\CollectionFactory;

class MassDelete extends Action
{
    const ADMIN_RESOURCE = 'Vendor_Module::entity_delete';
    
    protected $filter;
    protected $collectionFactory;
    
    public function __construct(
        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());
        $collectionSize = $collection->getSize();
        
        foreach ($collection as $item) {
            $item->delete();
        }
        
        $this->messageManager->addSuccessMessage(
            __('A total of %1 record(s) have been deleted.', $collectionSize)
        );
        
        return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('*/*/');
    }
}
Reference: See Magento\Cms\Controller\Adminhtml\Page\MassDelete and Magento\Cms\Controller\Adminhtml\Page\MassEnable.

Inline Editing

Inline editing allows users to edit grid data directly without opening a separate form.

Steps for Inline Editing

  1. Configure the editorConfig node in the listing XML file
  2. Configure the editor for each field
  3. Create a controller to save the result

1. Configure editorConfig

<columns name="entity_columns">
    <settings>
        <editorConfig>
            <param name="clientConfig" xsi:type="array">
                <item name="saveUrl" xsi:type="url" path="*/*/inlineEdit"/>
                <item name="validateBeforeSave" xsi:type="boolean">true</item>
            </param>
            <param name="indexField" xsi:type="string">entity_id</param>
            <param name="enabled" xsi:type="boolean">true</param>
            <param name="selectProvider" xsi:type="string">${ $.columnsProvider }.ids</param>
        </editorConfig>
    </settings>
</columns>

2. Configure Editor for Each Field

<column name="title">
    <settings>
        <filter>text</filter>
        <editor>
            <validation>
                <rule name="required-entry" xsi:type="boolean">true</rule>
            </validation>
            <editorType>text</editorType>
        </editor>
        <label translate="true">Title</label>
    </settings>
</column>

3. InlineEdit Controller

<?php
namespace Vendor\Module\Controller\Adminhtml\Entity;

use Magento\Backend\App\Action;
use Magento\Framework\Controller\Result\JsonFactory;

class InlineEdit extends Action
{
    const ADMIN_RESOURCE = 'Vendor_Module::entity_save';
    
    protected $jsonFactory;
    protected $entityFactory;
    
    public function execute()
    {
        $resultJson = $this->jsonFactory->create();
        $error = false;
        $messages = [];
        
        if ($this->getRequest()->getParam('isAjax')) {
            $postItems = $this->getRequest()->getParam('items', []);
            foreach (array_keys($postItems) as $entityId) {
                $entity = $this->entityFactory->create()->load($entityId);
                try {
                    $entity->setData(array_merge($entity->getData(), $postItems[$entityId]));
                    $entity->save();
                } catch (\Exception $e) {
                    $messages[] = __('[ID: %1] %2', $entity->getId(), $e->getMessage());
                    $error = true;
                }
            }
        }
        
        return $resultJson->setData(['messages' => $messages, 'error' => $error]);
    }
}
Reference: See Magento\Cms\Controller\Adminhtml\Page\InlineEdit.

Admin Forms

Admin forms follow a similar pattern to grids but are used for creating and editing individual records.

Steps to Create an Admin Form

  1. Create controllers for form display, edit, save, and delete actions
  2. May need additional controllers for file uploading
  3. Modify layout to include UiComponent's configuration
  4. Create form UI component XML file
  5. Create/configure DataSource for the form
Reference: See Magento/Cms/Controller/Adminhtml/Page/ for complete examples.

Form Layout XML

view/adminhtml/layout/entity_edit.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="styles"/>
    <body>
        <referenceContainer name="content">
            <uiComponent name="entity_form"/>
        </referenceContainer>
    </body>
</page>

Form UI Component XML

view/adminhtml/ui_component/entity_form.xml
<?xml version="1.0"?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">entity_form.entity_form_data_source</item>
        </item>
        <item name="label" xsi:type="string" translate="true">Entity Information</item>
    </argument>
    
    <dataSource name="entity_form_data_source">
        <dataProvider class="Vendor\Module\Model\Entity\DataProvider" name="entity_form_data_source">
            <settings>
                <requestFieldName>id</requestFieldName>
                <primaryFieldName>entity_id</primaryFieldName>
            </settings>
        </dataProvider>
    </dataSource>
    
    <fieldset name="general">
        <settings>
            <label translate="true">General Information</label>
        </settings>
        
        <field name="entity_id" formElement="input">
            <settings>
                <dataType>text</dataType>
                <visible>false</visible>
            </settings>
        </field>
        
        <field name="title" formElement="input">
            <settings>
                <validation>
                    <rule name="required-entry" xsi:type="boolean">true</rule>
                </validation>
                <dataType>text</dataType>
                <label translate="true">Title</label>
            </settings>
        </field>
        
        <field name="is_active" formElement="checkbox">
            <settings>
                <dataType>boolean</dataType>
                <label translate="true">Is Active</label>
            </settings>
            <formElements>
                <checkbox>
                    <settings>
                        <valueMap>
                            <map name="false" xsi:type="string">0</map>
                            <map name="true" xsi:type="string">1</map>
                        </valueMap>
                        <prefer>toggle</prefer>
                    </settings>
                </checkbox>
            </formElements>
        </field>
    </fieldset>
</form>

Key Components Summary

Component Purpose Location
Controller Handle requests, load data, return response Controller/Adminhtml/Entity/
Layout XML Define page structure, reference UI Component view/adminhtml/layout/
UI Component XML Configure grid/form structure, columns, fields view/adminhtml/ui_component/
DataSource (di.xml) Configure collection for grid/form data etc/di.xml
Grid Collection Fetch and filter data (implements SearchResultInterface) Model/ResourceModel/Entity/Grid/
Mass Action Controllers Handle bulk operations (delete, enable, disable) Controller/Adminhtml/Entity/Mass*
InlineEdit Controller Handle inline grid editing Controller/Adminhtml/Entity/InlineEdit
DataProvider Provide form data Model/Entity/DataProvider.php

Quick Reference: Grids vs Forms

Admin Grids

  • Display multiple records in table format
  • Use <listing> root element
  • Grid Collection implements SearchResultInterface
  • Support mass actions
  • Optional inline editing
  • Filtering, sorting, pagination built-in

Admin Forms

  • Edit single record
  • Use <form> root element
  • DataProvider fetches form data
  • Controllers: New, Edit, Save, Delete
  • May need file upload controllers
  • Field validation and dependencies

Exam Tips

Key Points to Remember

  • 6 steps to create a grid: Controller, Layout, UI Component XML, DataSource, Mass Actions, Inline Editing (optional)
  • Grid collections implement SearchResultInterface
  • DataSource name in di.xml must match UI Component XML
  • Mass actions configured in listing/listingToolbar/massaction/action nodes
  • Some mass actions trigger JS modules instead of immediate requests
  • Inline editing requires: editorConfig, field editor config, InlineEdit controller
  • Forms follow similar pattern to grids with different UI Component structure
  • Form controllers handle CRUD operations: New, Edit, Save, Delete
  • Layout XML is the same for both grids and forms - just reference the UI Component
  • Real examples: Magento_Cms module for CMS pages

Further Reading