How to Add Custom Grid into a Custom Tab in Product Edit Page in Magento 2 Admin
If you have been on this blog before, you probably read the post Create Custom Tab under Product Edit Section. In that post, you have seen how we have added an HTML to custom tab without creating a product attribute. After reading that post many readers have requested to write a post on How to Add Custom Grid into a Custom Tab in Product Edit Page in Magento 2 Admin.
Today, we will create a custom tab in product edit page and in that tab we will display a custom grid from another module.
Before we start Module development
- I assume that you have already created custom grid module. We do not cover how to create a custom grid module in Magento 2 admin in this post.
- Your Magento 2 cache is disabled.
Getting Started: Development
Step 1: Create module.xml file under the app/code/Codextblog/Customgridtab/etc
directory
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd"> <module name="Codextblog_Customgridtab" setup_version="1.0.0"></module> </config>
Step 2: Create registration.php file under the app/code/Codextblog/Customgridtab
directory
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Codextblog_Customgridtab', __DIR__ );
Step 3: Declare modifier in app/code/Codextblog/Customgridtab/etc/adminhtml/di.xml
file
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool"> <arguments> <argument name="modifiers" xsi:type="array"> <item name="custom-tab-grid-with-content" xsi:type="array"> <item name="class" xsi:type="string">Codextblog\Customgridtab\Ui\DataProvider\Product\Form\Modifier\CustomTab</item> <item name="sortOrder" xsi:type="number">10</item> </item> </argument> </arguments> </virtualType> </config>
In this file Under the <item> tag, we have passed a class file. This file will create a custom tab in product edit page.
Step 4: Create modifier class CustomTab under app/code/Codextblog/Customgridtab/Ui/Dataprovider/Product/Form/Modifier
directory
<?php namespace Codextblog\Customgridtab\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Model\Locator\LocatorInterface; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Framework\Stdlib\ArrayManager; use Magento\Framework\UrlInterface; use Magento\Ui\Component\Container; use Magento\Ui\Component\Form\Fieldset; class CustomTab extends AbstractModifier { const SAMPLE_FIELDSET_NAME = 'custom_grid_fieldset'; const SAMPLE_FIELD_NAME = 'custom_grid'; protected $_backendUrl; protected $_productloader; protected $_modelCustomgridFactory; /** * @var \Magento\Catalog\Model\Locator\LocatorInterface */ protected $locator; /** * @var ArrayManager */ protected $arrayManager; /** * @var UrlInterface */ protected $urlBuilder; /** * @var array */ protected $meta = []; /** * @param LocatorInterface $locator * @param ArrayManager $arrayManager * @param UrlInterface $urlBuilder */ public function __construct( LocatorInterface $locator, ArrayManager $arrayManager, UrlInterface $urlBuilder, \Magento\Catalog\Model\ProductFactory $_productloader, \Magento\Backend\Model\UrlInterface $backendUrl ) { $this->locator = $locator; $this->arrayManager = $arrayManager; $this->urlBuilder = $urlBuilder; $this->_productloader = $_productloader; $this->_backendUrl = $backendUrl; } public function modifyData(array $data) { return $data; } public function modifyMeta(array $meta) { $this->meta = $meta; $this->addCustomTab(); return $this->meta; } protected function addCustomTab() { $this->meta = array_merge_recursive( $this->meta, [ static::SAMPLE_FIELDSET_NAME => $this->getTabConfig(), ] ); } protected function getTabConfig() { return [ 'arguments' => [ 'data' => [ 'config' => [ 'label' => __('Custom Grid Tab'), 'componentType' => Fieldset::NAME, 'dataScope' => '', 'provider' => static::FORM_NAME . '.product_form_data_source', 'ns' => static::FORM_NAME, 'collapsible' => true, ], ], ], 'children' => [ static::SAMPLE_FIELD_NAME => [ 'arguments' => [ 'data' => [ 'config' => [ 'autoRender' => true, 'componentType' => 'insertListing', 'dataScope' => 'custom_grid_listing', 'externalProvider' => 'custom_grid_listing.custom_grid_listing_data_source', 'selectionsProvider' => 'custom_grid_listing.custom_grid_listing.product_columns.ids', 'ns' => 'custom_grid_listing', 'render_url' => $this->urlBuilder->getUrl('mui/index/render'), 'realTimeLink' => false, 'behaviourType' => 'simple', 'externalFilterMode' => true, 'imports' => [ 'productId' => '${ $.provider }:data.product.current_product_id' ], 'exports' => [ 'productId' => '${ $.externalProvider }:params.current_product_id' ], ], ], ], 'children' => [], ], ], ]; } }
This file is responsible for creating a custom tab. Function getTabConfig return an array that contains all the information which are needed to create a custom tab. In this function in line no 106 we have defined node ‘componentType’ with value as ‘insertlisting’ that means our tab will display the listing. In the previous post, we have defined the ‘componentType’ as a field because we need only filed to display but here we need to display grid so we have passed a value as ‘insertlisting’.
In line no 107, we have passed node ‘dataScope’ with value as ‘custom_grid_listing’ indicates the name of our ui_component element. Next we will define ui_component file custom_grid_listing.xml
Step 5: Create ui component xml file custom_grid_listing.xml under app/code/Codextblog/Customgridtab/view/adminhtml/ui_component
directory
<?xml version="1.0" encoding="UTF-8"?> <!-- /** * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ --> <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">custom_grid_listing.custom_grid_listing_data_source</item> <item name="deps" xsi:type="string">custom_grid_listing.custom_grid_listing_data_source</item> </item> <item name="spinner" xsi:type="string">custom_grid_columns</item> </argument> <dataSource name="custom_grid_listing_data_source"> <argument name="dataProvider" xsi:type="configurableObject"> <argument name="class" xsi:type="string">Codextblog\Customgridtab\Ui\DataProvider\Product\CustomDataProvider</argument> <argument name="name" xsi:type="string">custom_grid_listing_data_source</argument> <argument name="primaryFieldName" xsi:type="string">id</argument> <argument name="requestFieldName" xsi:type="string">id</argument> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item> <item name="update_url" xsi:type="url" path="mui/index/render"/> <item name="storageConfig" xsi:type="array"> <item name="cacheRequests" xsi:type="boolean">false</item> </item> </item> </argument> </argument> </dataSource> <listingToolbar name="listing_top"> <filters name="listing_filters"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="statefull" xsi:type="array"> <item name="applied" xsi:type="boolean">false</item> </item> <item name="params" xsi:type="array"> <item name="filters_modifier" xsi:type="array"/> </item> </item> </argument> </filters> <paging name="listing_paging"/> </listingToolbar> <columns name="custom_grid_columns" class="Magento\Ui\Component\Listing\Columns"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="childDefaults" xsi:type="array"> <item name="fieldAction" xsi:type="array"> <item name="provider" xsi:type="string">customGrid</item> <item name="target" xsi:type="string">selectData</item> <item name="params" xsi:type="array"> <item name="0" xsi:type="string">${ $.$data.rowIndex }</item> </item> </item> </item> </item> </argument> <column name="id"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">textRange</item> <item name="sorting" xsi:type="string">asc</item> <item name="label" xsi:type="string" translate="true">ID</item> <item name="sortOrder" xsi:type="number">0</item> </item> </argument> </column> <column name="title"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">text</item> <item name="label" xsi:type="string" translate="true">Title</item> <item name="sortOrder" xsi:type="number">10</item> <item name="truncate" xsi:type="number">50</item> <item name="nl2br" xsi:type="boolean">true</item> <item name="escape" xsi:type="boolean">true</item> </item> </argument> </column> <column name="is_active" class="Codextblog\Customgridtab\Ui\Component\Listing\Columns\Active"> <argument name="data" xsi:type="array"> <item name="options" xsi:type="object">Codextblog\Customgridtab\Ui\Component\Listing\Columns\Active</item> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">select</item> <item name="dataType" xsi:type="string">select</item> <item name="label" xsi:type="string" translate="true">Active</item> <item name="sortOrder" xsi:type="number">20</item> </item> </argument> </column> <column name="date_created" class="Magento\Ui\Component\Listing\Columns\Date"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">dateRange</item> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item> <item name="dataType" xsi:type="string">date</item> <item name="label" xsi:type="string" translate="true">Created At</item> <item name="sortOrder" xsi:type="number">30</item> </item> </argument> </column> </columns> </listing>
Let’s understand this file. The very important tag in this file is <dataSource> that contains the following arguments
- class: define the DataProvider class
Codextblog\Customgridtab\Ui\DataProvider\Product\CustomDataProvider
which provide the collection for rendering the grid. We will create this class in next step. - name: define unique name
- primaryFieldName: define the primary field of a database table which we use in our collection. In our case it is ‘id’.
- requestFieldName: same as primaryFieldName
- data: define some of the Magento 2 inbuilt data parameter to render the grid
Here other important tags are <listingtoolbar> and <columns> which are responsible for render toolbar (like pagination, filter) and display defined columns in the grid respectively. In the <columns> we have defined id, title, is_active and date_created so our grid will display only these four columns in the grid. We can add as many columns as we want. We just need to defined columns name with type under <columns> tag.
In one of the columns, we have defined is_active field. This field will render the drop-down with options. To render those options we have provided the class Codextblog\Customgridtab\Ui\Component\Listing\Columns\Active
step 6: Let’s create this class Active.php under app/code/Codextblog/Customgridtab/Ui/Component/Listing/Columns
directory
<?php /** * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Codextblog\Customgridtab\Ui\Component\Listing\Columns; use Magento\Framework\Data\OptionSourceInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\Column; use Magento\Framework\View\Element\UiComponent\ContextInterface; /** * Class Active */ class Active extends Column implements OptionSourceInterface { /** * @param ContextInterface $context * @param UiComponentFactory $uiComponentFactory * @param StatusSource $source * @param array $components * @param array $data */ public function __construct( ContextInterface $context, UiComponentFactory $uiComponentFactory, array $components = [], array $data = [] ) { parent::__construct($context, $uiComponentFactory, $components, $data); } /** * {@inheritdoc} */ public function prepareDataSource(array $dataSource) { $dataSource = parent::prepareDataSource($dataSource); $options = [ 1 => __('Yes'), 2 => __('No') ]; if (empty($dataSource['data']['items'])) { return $dataSource; } foreach ($dataSource['data']['items'] as &$item) { if (isset($options[$item['is_active']])) { $item['is_active'] = $options[$item['is_active']]; } } return $dataSource; } /** * {@inheritdoc} */ public function toOptionArray() { $result = []; $result[] = ['value' => 1, 'label' => 'Yes']; $result[] = ['value' => 2, 'label' => 'No']; return $result; } }
Step 7: Create Dataprovider class CustomDataProvider.php under app/code/Codextblog/Customgridtab/Ui/DataProvider/Product
directory
<?php /** * Copyright © 2013-2017 Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Codextblog\Customgridtab\Ui\DataProvider\Product; use Magento\Framework\App\RequestInterface; use Magento\Ui\DataProvider\AbstractDataProvider; use Codextblog\Customgrid\Model\ResourceModel\Post\CollectionFactory; use Codextblog\Customgrid\Model\ResourceModel\Post\Collection; use Codextblog\Customgrid\Model\Post; /** * Class CustomDataProvider * * @method Collection getCollection */ class CustomDataProvider extends AbstractDataProvider { /** * @var CollectionFactory */ protected $collectionFactory; /** * @var RequestInterface */ protected $request; /** * @param string $name * @param string $primaryFieldName * @param string $requestFieldName * @param CollectionFactory $collectionFactory * @param RequestInterface $request * @param array $meta * @param array $data */ public function __construct( $name, $primaryFieldName, $requestFieldName, CollectionFactory $collectionFactory, RequestInterface $request, array $meta = [], array $data = [] ) { parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); $this->collectionFactory = $collectionFactory; $this->collection = $this->collectionFactory->create(); $this->request = $request; } /** * {@inheritdoc} */ public function getData() { $this->getCollection(); $arrItems = [ 'totalRecords' => $this->getCollection()->getSize(), 'items' => [], ]; foreach ($this->getCollection() as $item) { $arrItems['items'][] = $item->toArray([]); } return $arrItems; } /** * {@inheritdoc} */ public function addFilter(\Magento\Framework\Api\Filter $filter) { $field = $filter->getField(); if (in_array($field, ['id','title','created_at', 'is_active'])) { $filter->setField($field); } parent::addFilter($filter); } }
In this file you can see, We have added the Codextblog\Customgrid\Model\ResourceModel\Post\CollectionFactory
as a dependency in the constructor. This is the CollectionFactory class of another module called Customgrid. This module grid we will render in our custom tab. Here function getData provides the data array which Magento 2 use to render the grid data. Function addFilter is responsible for providing filter data whenever we apply a filter in the grid.
That’s it. After following all the steps, run following commands and check your Magento 2 admin product edit page. You will see the “Custom Grid Tab” that render the grid.
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy
php bin/magento c:c
If you liked this post, then please like us on Facebook and follow us on Twitter.
Leave a Comment
(28 Comments)
thank you for your amazing efforts, however, I’m facing an issue while saving the selected values from the grid, do you have any idea on how to manage to save the values ? or any idea how can i catch the selected values by the admin ?
Thank you in advanced
how can i filter this by current product id
Hi I have added custom grid in product edit but not able to filter by grid data with product id, can you please explain how filter product edit grid data with product id.
i want to Custom Tab in Custom Model Edit Page in admin.please help me
Hi,
Was following your example, but steps 4, 6 and 7 have incomplete code.
Best regards
Did u acheive grid serializer
Not yet. I think grid serialise is not possible as same as Magento 1. You need to think other way like related products implementation.
i am getting error “Attention Something went wrong ” and after having checked with exception logs it says
Source class \”\\Codextblog\\Customgrid\\Model\\ResourceModel\\Post\\Collection\” for \”Codextblog\\Customgrid\\Model\\ResourceModel\\Post\\CollectionFactory\” generation does not exist.
so I realize the dependency injection you used from Codextblog\\Customgrid is not being compensated over here, can you guide what shall i do?
Have you run code generator command after implementing this code? I think code is not generated in generation directory.
You are right, there is nothing related to this module in a generated folder, but I don’t get it, I removed generated and removed module entry from DB and reinstalled it, and followed each command provided by you but still no generation.
this Codextblog\Customgrid is a different module than the one here, I think this module causing issue, can you please provide this module?
hello sir
i am getting error “Attention Something went wrong ”
what would be the issue?
Please check the error in ajax request from chrome dev tools under the network tab.
I need to filter the custom grid by product id, so that I am trying to get the product id from the URL by $this->getRequest()->getParam(‘id’, false), but I am always getting the empty result, I also included the Http request in the constructor method and tried to get the current product id from the registry, but there is no luck.
How to get the product id from the custom grid of admin
Hi iam facing same problem how to load grid filter by product id
Yes working awesome
Hi how load grid filter by product id I am facing same problem
How you are able to get “current product id” in getData function of your DataProvider file please share how can we load the current product id.
Try this
I tried this but i am getting NULL value.
How can we add a button here to open a modal?
My page is keep loading but no render the grid or tab after i follow your post ..
Please check the request URL, you will find an error that causes this issue.
HI Chirag Dodia,
Did u achieve grid serializer .
Even i am stuck with it..
Thanks
Not yet. Will write a post once succeed with grid serialize.
please help
grid is showing properly but what if i have to add checkbox selection option and that selection value need to store in database
Hello Hiren,
To display checkbox selection and save the value in the database is a part of grid serialization that is not within the scope of this post. Will check how we can achieve that. If I succeed I will write a separate post on it.
Did u acheive grid serializer
Useful Magento 2 Articles
Author Info
Chirag
Connect With MeWas this post helpful? Please support Us!
To Avoid Spam Downloads, We Want Your Email
away. Please submit form below.
Magento 2
Knowledge
to your Inbox