Magento 2: Create UiComponent List and UiComponent Form by Example

This Module will show you how to create Magento 2 backend grid list and edit form with a full-fledged example.

Posted on January 1, 2019 in Magento2

1. Create a new Module, named: Jeff_Contacts

I will use the contact information as an example to show you how to create backend UiComponent grid list and UiComponent Edit Form. First we need to create the basic Magento 2 module:

module.xml file:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Jeff_Contacts" setup_version="0.0.1"/>
</config>

registration.php file:

<?php
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'Jeff_Contacts',
        __DIR__
    );

Setup Script, InstallSchema.php:

<?php
namespace Jeff\Contacts\Setup;
class InstallSchema implements \Magento\Framework\Setup\InstallSchemaInterface
{
    public function install(\Magento\Framework\Setup\SchemaSetupInterface $setup, \Magento\Framework\Setup\ModuleContextInterface $context)
    {
        $installer = $setup;
        $installer->startSetup();
        //START: install stuff
        //END:   install stuff
        
        //START table setup
        $table = $installer->getConnection()->newTable(
            $installer->getTable('jeff_contacts_contact')
        )->addColumn(
            'jeff_contacts_contact_id',
            \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
            null,
            [ 'identity' => true, 'nullable' => false, 'primary' => true, 'unsigned' => true, ],
            'Entity ID'
        )->addColumn(
            'name',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            [ 'nullable' => false, ],
            'Demo name'
        )->addColumn(
            'email',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            [ 'nullable' => false, ],
            'Demo email'
        )->addColumn(
            'comment',
            \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
            255,
            [ 'nullable' => false, 'default' => '0' ],
            'Demo comment' 
        )->addColumn(
            'creation_time',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            [ 'nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT, ],
            'Creation Time'
        )->addColumn(
            'update_time',
            \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
            null,
            [ 'nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT_UPDATE, ],
            'Modification Time'
        )->addColumn(
            'is_active',
            \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
            null,
            [ 'nullable' => false, 'default' => '1', ],
            'Is Active'
        );
        $installer->getConnection()->createTable($table);
        //END   table setup
$installer->endSetup();
    }
}

The database table structure is like as following:

+--------------------------+------------------+------+-----+-------------------+-----------------------------+
| Field                    | Type             | Null | Key | Default           | Extra                       |
+--------------------------+------------------+------+-----+-------------------+-----------------------------+
| jeff_contacts_contact_id | int(10) unsigned | NO   | PRI | NULL              | auto_increment              |
| name                     | varchar(255)     | NO   |     | NULL              |                             |
| email                    | varchar(255)     | NO   |     | NULL              |                             |
| comment                  | varchar(255)     | NO   |     | 0                 |                             |
| creation_time            | timestamp        | NO   |     | CURRENT_TIMESTAMP |                             |
| update_time              | timestamp        | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| is_active                | smallint(6)      | NO   |     | 1                 |                             |
+--------------------------+------------------+------+-----+-------------------+-----------------------------+

Contact model:

<?php
namespace Jeff\Contacts\Model;
class Contact extends \Magento\Framework\Model\AbstractModel implements \Jeff\Contacts\Api\Data\ContactInterface, \Magento\Framework\DataObject\IdentityInterface
{
    const CACHE_TAG = 'jeff_contacts_contact';

    protected function _construct()
    {
        $this->_init('Jeff\Contacts\Model\ResourceModel\Contact');
    }

    public function getIdentities()
    {
        return [self::CACHE_TAG . '_' . $this->getId()];
    }
}

ResourceModel for Contact

<?php
namespace Jeff\Contacts\Model\ResourceModel;

class Contact extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
    protected function _construct()
    {
        $this->_init('jeff_contacts_contact','jeff_contacts_contact_id');
    }
}

Contact Collection

<?php
namespace Jeff\Contacts\Model\ResourceModel\Contact;
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
    protected $_idFieldName = 'jeff_contacts_contact_id';

    protected function _construct()
    {
        $this->_init('Jeff\Contacts\Model\Contact','Jeff\Contacts\Model\ResourceModel\Contact');
    }
}

backend routers.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="jeff_contacts" frontName="jeff_contacts">
            <module name="Jeff_Contacts"/>
        </route>
    </router>
</config>

menu.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
    <add id="Jeff_Contacts::unique_identifier" 
        resource="Jeff_Contacts::unique_identifier" 
        title="My Contacts" 
        action="jeff_contacts/index/index" 
        module="Jeff_Contacts" 
        sortOrder="999" 
        parent="Magento_Backend::content"
        dependsOnModule="Magento_Backend"/>
    </menu>
</config>

Index controller file

<?php
namespace Jeff\Contacts\Controller\Adminhtml\Index;

class Index extends \Magento\Backend\App\Action
{

    const ADMIN_RESOURCE = 'Index';

    protected $resultPageFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory)
    {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        return $this->resultPageFactory->create();
    }
}

2 UiComponent Grid List:

backend layout: jeff_contacts_index_index.xml

#app/code/Jeff/Contacts/view/adminhtml/layout/jeff_contacts_index_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">
<referenceBlock name="content">
    <block template="content.phtml" class="Jeff\Contacts\Block\Adminhtml\Main" name="jeff_contacts_block_main"/>
    <uiComponent name="contacts_test_listing" />
</referenceBlock></page>

From this layout file, we can find out that we need to generate three more files, backend block file: Main.php and template file for the block: content.phtml and our own uiComponent: contacts_test_listing.xml

Main.php

#app/code/Jeff/Contacts/Block/Adminhtml/Main.php

<?php
namespace Jeff\Contacts\Block\Adminhtml;
class Main extends \Magento\Backend\Block\Template
{
    function _prepareLayout(){}
}

content.phtml

#app/code/Jeff/Contacts/view/adminhtml/templates/content.html

<h1>Contacts</h1>

uiComponent definition: contacts_test_listing.xml

#app/code/Jeff/Contacts/view/adminhtml/ui_component/contacts_test_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">
        <!-- Here we declare the data_source to use with the tag 'js_config', which makes the links between your grid and the database -->
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">contacts_test_listing.contacts_test_listing_data_source</item>
            <item name="deps" xsi:type="string">contacts_test_listing.contacts_test_listing_data_source</item>
        </item>

        <item name="spinner" xsi:type="string">contacts_test_columns</item>

        <item name="buttons" xsi:type="array">
            <item name="add" xsi:type="array">
                <item name="name" xsi:type="string">add</item>
                <item name="label" xsi:type="string" translate="true">Add a new Contact</item>
                <item name="class" xsi:type="string">primary</item>
                <item name="url" xsi:type="string">*/*/newAction</item>
            </item>
        </item>
    </argument>

    <!-- dataProvider, with a class tag to define the name of the object to be used. -->
    <dataSource name="contacts_contact_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">

            <argument name="class" xsi:type="string">ContactsGridDataProvider</argument>
            <argument name="name" xsi:type="string">contacts_test_listing_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">jeff_contacts_contact_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="indexField" xsi:type="string">jeff_contacts_contact_id</item>
                    </item>
                </item>
            </argument>
        </argument>
    </dataSource>

    <!-- define the columns of grid: name from spinner -->
    <columns name="contacts_test_columns">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <!-- bookmark behavior -->
                <item name="storageConfig" xsi:type="array">
                    <item name="provider" xsi:type="string">contacts_test_listing.contacts_test_listing.listing_top.bookmarks</item>
                    <item name="namespace" xsi:type="string">current</item>
                </item>
                <item name="childDefaults" xsi:type="array">
                    <item name="controlVisibility" xsi:type="boolean">true</item>
                    <item name="storageConfig" xsi:type="array">
                        <item name="provider" xsi:type="string">contacts_test_listing.contacts_test_listing.listing_top.bookmarks</item>
                        <item name="root" xsi:type="string">columns.${ $.index }</item>
                        <!-- ${ $.index } means the current column index -->
                        <!-- ${ $.storageConfig.root }: It is referring to root property of current storageConfig
                            that you previously defined as ${ $.index }.so, at the end it should take the same value as ${ $.index} -->
                        <item name="namespace" xsi:type="string">current.${ $.storageConfig.root}</item>
                    </item>
                </item>
            </item>
        </argument>
        <selectionsColumn name="ids">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">jeff_contacts_contact_id</item>
                </item>
            </argument>
        </selectionsColumn>

        <!-- Column ID -->
        <column name="jeff_contacts_contact_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>
            </argument>
        </column>

        <!-- Column name -->
        <column name="name">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">contact</item>
                    <item name="dataScope" xsi:type="string">name</item>
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Name</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </column>

        <!-- Column email -->
        <column name="email">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">contact</item>
                    <item name="dataScope" xsi:type="string">email</item>
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Email</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </column>

        <column name="comment">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">contact</item>
                    <item name="dataScope" xsi:type="string">comment</item>
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Comment</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">false</item>
                    </item>
                </item>
            </argument>
        </column>

        <!-- action columns edit and delete -->
        <actionsColumn name="actions" class="Jeff\Contacts\Ui\Component\Listing\Column\ContactsActions">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="resizeEnabled" xsi:type="boolean">false</item>
                    <item name="resizeDefaultWidth" xsi:type="string">107</item>
                    <item name="indexField" xsi:type="string">pfay_contacts_id</item>
                </item>
            </argument>
        </actionsColumn>
    </columns>

</listing>

This file contains three main sections: configuration, dataProvider and columns. Configuration section: Here we declare the data_source to use with the tag ‘js_config’, which makes the links between your grid and the database.

UiComponent Column for ActionsColumn:

<?php
namespace Jeff\Contacts\Ui\Component\Listing\Column;

use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
use Magento\Framework\UrlInterface;

class ContactsActions extends \Magento\Ui\Component\Listing\Columns\Column
{
    protected $urlBuilder;

    public function __construct(ContextInterface $context, UiComponentFactory $uiComponentFactory, UrlInterface $urlBuilder, array $components=[], array $data=[])
    {
        $this->urlBuilder = $urlBuilder;
        parent::__construct($context, $uiComponentFactory, $components, $data);
    }

    public function prepareDataSource(array $dataSource)
    {
        if(isset($dataSource['data']['items'])) {
            foreach($dataSource['data']['items'] as &$item)
            {
                $item[$this->getData('name')]['edit'] = [
                    'href' => $this->urlBuilder->getUrl('jeff_contacts/index/edit', ['id' => $item['jeff_contacts_contact_id']]),
                    'label' => __('Edit'),
                    'hidden' => false
                ];
                $item[$this->getData('name')]['delete'] = [
                    'href' => $this->urlBuilder->getUrl('jeff_contacts/index/delete', ['id' => $item['jeff_contacts_contact_id']]),
                    'label' => __('Delete'),
                    'hidden' => false
                ];
            }
        }

        return $dataSource;
    }
}

Connecting model and grid by creating 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">
                  <!-- ContactsGridDataProvider -->
    <virtualType name="ContactsGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
        <arguments>
            <argument name="collection" xsi:type="object" shared="false">Jeff\Contacts\Model\ResourceModel\Contact\Collection</argument>
            <argument name="filterPool" xsi:type="object" shared="false">ContactsGridFilterPool</argument>
        </arguments>
    </virtualType>

    <virtualType name="ContactsGridFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool">
        <arguments>
            <argument name="appliers" xsi:type="array">
                <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item>
                <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item>
            </argument>
        </arguments>
    </virtualType>

    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="contacts_test_listing_data_source" xsi:type="string">Jeff\Contacts\Model\ResourceModel\Contact\Grid\Collection</item>
            </argument>
        </arguments>
    </type>

    <virtualType name="Jeff\Contacts\Model\ResourceModel\Contact\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult">
        <arguments>
            <argument name="mainTable" xsi:type="string">jeff_contacts_contact</argument>
            <argument name="resourceModel" xsi:type="string">Jeff\Contacts\Model\ResourceModel\Contact</argument>
        </arguments>
    </virtualType>
</config>

3 UiComponent edit Form

Create NewAction action file:

<?php
namespace Jeff\Contacts\Controller\Adminhtml\Index;

class NewAction extends \Magento\Backend\App\Action
{

    const ADMIN_RESOURCE = 'Index';

    protected $resultPageFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory)
    {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        return $this->resultPageFactory->create();
    }
}

handler file for jeff_contacts_index_newaction action

<?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="jeff_contacts_index_edit" />
</page>

update handler file: jeff_contacts_index_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" />
    <update handle="editor" />
    <body>
        <referenceContainer name="content">
            <uiComponent name="jeff_contacts_form" />
        </referenceContainer>
    </body>
</page>

Here, we defined our uiComponent edit file and we will create it next.

uiComponent Form definition

#app/code/Jeff/Contacts/view/adminhtml/ui_component/jeff_contacts_form.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">jeff_contacts_form.jeff_contacts_form_data_source</item>
            <item name="deps" xsi:type="string">jeff_contacts_form.jeff_contacts_form_data_source</item>
        </item>

        <item name="label" xsi:type="string" translate="true">Contact Form</item>
        <!--
        <item name="layout" xsi:type="array">
            <item name="type" xsi:type="string">tabs</item>
        </item>
        -->
        <item name="config" xsi:type="array">
            <item name="dataScope" xsi:type="string">data</item>
            <item name="namespace" xsi:type="string">contact_form</item>
        </item>

        <item name="template" xsi:type="string">templates/form/collapsible</item>

        <item name="buttons" xsi:type="array">
            <item name="back" xsi:type="string">Jeff\Contacts\Block\Adminhtml\Contact\Edit\BackButton</item>
            <item name="delete" xsi:type="string">Jeff\Contacts\Block\Adminhtml\Contact\Edit\DeleteButton</item>
            <item name="reset" xsi:type="string">Jeff\Contacts\Block\Adminhtml\Contact\Edit\ResetButton</item>
            <item name="save" xsi:type="string">Jeff\Contacts\Block\Adminhtml\Contact\Edit\SaveButton</item>
        </item>
    </argument>

    <dataSource name="jeff_contacts_form_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Jeff\Contacts\Model\Contact\DataProvider</argument>
            <argument name="name" xsi:type="string">jeff_contacts_form_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">jeff_contacts_contact_id</argument>
            <argument name="requestFieldName" xsi:type="string">id</argument>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="submit_url" xsi:type="url" path="jeff_contacts/index/save" />
                </item>
            </argument>
        </argument>
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
            </item>
        </argument>
    </dataSource>

    <fieldset name="contact">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="collapsible" xsi:type="boolean">false</item>
                <item name="label" xsi:type="string" translate="true">Contact Fieldset</item>
            </item>
        </argument>

        <field name="jeff_contacts_contact_id">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">false</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">contact</item>
                </item>
            </argument>
        </field>

        <field name="name">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string">Name</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">contact</item>
                </item>
            </argument>
        </field>

        <field name="email">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string">Email</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">contact</item>
                </item>
            </argument>
        </field>

        <field name="comment">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string">Comment</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">contact</item>
                </item>
            </argument>
        </field>
    </fieldset>
</form>

Four Button Blocks: Back, Delete, Reset and Save

BackButton.php:


<?php
namespace Jeff\Contacts\Block\Adminhtml\Contact\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

class BackButton extends GenericButton implements ButtonProviderInterface
{
    public function getButtonData()
    {
        return [
            'label' => __('Back'),
            'on_click' => sprintf("location.href= '%s';", $this->getBackUrl()),
            'class' => 'back',
            'sort_order' => 10
        ];
    }

    public function getBackUrl()
    {
        return $this->getUrl('*/*/');
    }
}

DeleteButton.php:


<?php
namespace Jeff\Contacts\Block\Adminhtml\Contact\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

class DeleteButton extends GenericButton implements ButtonProviderInterface
{
    public function getButtonData()
    {
        return [
            'label' => __('Delete Contact'),
            'on_click' => 'deleteConfirm(\'' . __('Are you sure you want to delete this contact ?') . '\', \'' . $this->getDeleteUrl() . '\')',
            'class' => 'delete',
            'sort_order' => 20
        ];
    }

    public function getDeleteUrl()
    {
        $urlInterface = \Magento\Framework\App\ObjectManager::getInstance()->get('Magento\Framework\UrlInterface');
        $url = $urlInterface->getCurrentUrl();

        $parts = explode('/', parse_url($url, PHP_URL_PATH));

        $id = $parts[6];

        return $this->getUrl('*/*/delete', ['id' => $id]);
    }
}

ResetButton.php:


<?php
namespace Jeff\Contacts\Block\Adminhtml\Contact\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

class ResetButton extends GenericButton implements ButtonProviderInterface
{
    public function getButtonData()
    {
        return [
            'label' => __('Reset'),
            'on_click' => 'javascript: location.reload();',
            'class' => 'reset',
            'sort_order' => 30
        ];
    }
}

SaveButton.php:


<?php
namespace Jeff\Contacts\Block\Adminhtml\Contact\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

class SaveButton extends GenericButton implements ButtonProviderInterface
{
    public function getButtonData()
    {
        return [
            'label' => __('Save Contact'),
            'class' => 'save primary',
            'data_attribute' => [
                'mage-init' => ['button' => ['event' => 'save']],
                'form-role' => 'save',
            ],
            //'on_click' => sprintf("location.href= '%s';", $this->getSaveUrl()),
            'sort_order' => 90
        ];
    }

    public function getSaveUrl()
    {
        return $this->getUrl('*/*/save', []) ;
    }
}

data provider file: DataProvider.php

<?php
namespace Jeff\Contacts\Model\Contact;

use Jeff\Contacts\Model\ResourceModel\Contact\CollectionFactory;
use Jeff\Contacts\Model\Contact;

class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
{
    protected $collection;
    protected $_loadedData;

    public function __construct(
        $name,
        $primaryFieldName,
        $requestFieldName,
        CollectionFactory $contactCollectionFactory,
        array $meta = [],
        array $data = []
    ){
        $this->collection = $contactCollectionFactory->create();
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
    }

    public function getData()
    {
        if(isset($this->_loadedData)) {
            return $this->_loadedData;
        }

        $items = $this->collection->getItems();

        foreach($items as $contact)
        {
            $this->_loadedData[$contact->getId()] = $contact->getData();
        }

        return $this->_loadedData;
    }

}

Edit Controller

<?php
namespace Jeff\Contacts\Controller\Adminhtml\Index;

class Edit extends \Magento\Backend\App\Action
{

    const ADMIN_RESOURCE = 'Index';

    protected $resultPageFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory)
    {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        return $this->resultPageFactory->create();
    }
}

Save Controller

<?php
namespace Jeff\Contacts\Controller\Adminhtml\Index;

class Save extends \Magento\Backend\App\Action
{

    const ADMIN_RESOURCE = 'Index';

    protected $resultPageFactory;
    protected $contactFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Jeff\Contacts\Model\ContactFactory $contactFactory
    )
    {
        $this->resultPageFactory = $resultPageFactory;
        $this->contactFactory = $contactFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        $resultRedirect = $this->resultRedirectFactory->create();
        $data = $this->getRequest()->getPostValue();

        if($data)
        {
            try{
                $id = $data['jeff_contacts_contact_id'];

                $contact = $this->contactFactory->create()->load($id);

                $data = array_filter($data, function($value) {return $value !== ''; });

                $contact->setData($data);
                $contact->save();
                $this->messageManager->addSuccess(__('Successfully saved the item.'));
                $this->_objectManager->get('Magento\Backend\Model\Session')->setFormData(false);
                return $resultRedirect->setPath('*/*/');
            }
            catch(\Exception $d)
            {
                $this->messageManager->addError($e->getMessage());
                $this->_objectManager->get('Magento\Backend\Model\Session')->setFormData($data);
                return $resultRedirect->setPath('*/*/edit', ['id' => $contact->getId()]);
            }
        }

         return $resultRedirect->setPath('*/*/');
    }
}

Delete Controller

<?php
namespace Jeff\Contacts\Controller\Adminhtml\Index;

use Jeff\Contacts\Model\Contact as Contact;


class Delete extends \Magento\Backend\App\Action
{

    const ADMIN_RESOURCE = 'Index';

    protected $resultPageFactory;
    protected $contactFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Jeff\Contacts\Model\ContactFactory $contactFactory
    )
    {
        $this->resultPageFactory = $resultPageFactory;
        $this->contactFactory = $contactFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        $id = $this->getRequest()->getParam('id');

        $contact = $this->contactFactory->create()->load($id);

        if(!$contact)
        {
            $this->messageManager->addError(__('Unable to process. please, try again.'));
            $resultRedirect = $this->resultRedirectFactory->create();
            return $resultRedirect->setPath('*/*/', array('_current' => true));
        }

        try{
            $contact->delete();
            $this->messageManager->addSuccess(__('Your contact has been deleted !'));
        }
        catch(\Exception $e)
        {
            $this->messageManager->addError(__('Error while trying to delete contact'));
            $resultRedirect = $this->resultRedirectFactory->create();
            return $resultRedirect->setPath('*/*/index', array('_current' => true));
        }

        $resultRedirect = $this->resultRedirectFactory->create();
        return $resultRedirect->setPath('*/*/index', array('_current' => true));
    }
}

4 Final Results: some images:

custom linked product


custom linked product


custom linked product


custom linked product


custom linked product


You can download the source code at HERE


comments powered by Disqus