Create UI Form in Magento 2

As we know starting from Magento 2, most of the new admin console is built on top of UI component. This is intended to combine HTML and JavaScript content, allowing the creation of new components with any form and function possible. In the previous post we created a simple grid with UI component and in this article we will continue to use it to create UI Form in Magento backend.

Here, we continue to use the simple module built in the previous post and build the create/edit message form in the backend.

1. Create admin controller

Create NewAction

Create file app\code\Magerubik\Simple\Controller\Adminhtml\Message\NewAction.php with below content.

<?php
namespace Magerubik\Simple\Controller\Adminhtml\Message;
class NewAction extends \Magento\Backend\App\Action
{
    public function execute()
    {
        $this->_forward('edit');
    }
}

This controller will redirect to edit page.

Create Edit controller

Create file app\code\Magerubik\Simple\Controller\Adminhtml\Message\Edit.php with below content.

<?php
namespace Magerubik\Simple\Controller\Adminhtml\Message;
class Edit extends \Magento\Backend\App\Action
{
  protected $resultPageFactory;

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

  public function execute()
  {
    $resultPage = $this->resultPageFactory->create();
    $resultPage->getConfig()->getTitle()->prepend(__('Message'));
    return $resultPage;
  }	
}

2. Create Edit page Layout

Create file app\code\Magerubik\Simple\view\adminhtml\layout\simple_message_edit.xml with below content.

<?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="editor"/>
    <body>
        <referenceContainer name="content">
            <uiComponent name="message_send_form"/>
        </referenceContainer>
    </body>
</page>

3. Create Ui form

Create file app\code\Magerubik\Simple\view\adminhtml\ui_component\message_send_form.xml with below content.

<?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">message_send_form.message_send_form_data_source</item>
            <item name="deps" xsi:type="string">message_send_form.message_send_form_data_source</item>
        </item>
        <item name="label" xsi:type="string" translate="true">Send Message</item>
        <item name="config" xsi:type="array">
            <item name="dataScope" xsi:type="string">data</item>
            <item name="namespace" xsi:type="string">message_send_form</item>
        </item>
        <item name="template" xsi:type="string">templates/form/collapsible</item>
        <item name="buttons" xsi:type="array">
            <item name="back" xsi:type="array">
                <item name="name" xsi:type="string">back</item>
                <item name="label" xsi:type="string" translate="true">Back</item>
                <item name="class" xsi:type="string">back</item>
                <item name="url" xsi:type="string">*/*/</item>
            </item>
            <item name="save" xsi:type="string">Magerubik\Simple\Block\Adminhtml\Button\Save</item>
        </item>
    </argument>
    <dataSource name="message_send_form_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Magerubik\Simple\Model\DataProvider\MessageDataProvider</argument>
            <argument name="name" xsi:type="string">message_send_form_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">messages_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="*/message/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="general">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
				<item name="label" xsi:type="string" translate="true"></item>
				<item name="collapsible" xsi:type="boolean">false</item>
                <item name="sortOrder" xsi:type="number">10</item>
            </item>
        </argument>
        <field name="messages_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="dataScope" xsi:type="string">messages_id</item>
                    </item>
                </argument>
            </field>
			<field name="user_id">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
						<item name="label" xsi:type="string" translate="true">user id</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="dataScope" xsi:type="string">user_id</item>
                    </item>
                </argument>
            </field>
            <field name="title">
				<argument name="data" xsi:type="array">
					<item name="config" xsi:type="array">
						<item name="label" xsi:type="string" translate="true">Title</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="dataScope" xsi:type="string">title</item>
						<item name="validation" xsi:type="array">
							<item name="required-entry" xsi:type="boolean">true</item>
						</item>
					</item>
				</argument>
			</field>
			<field name="description">
				<argument name="data" xsi:type="array">
					<item name="config" xsi:type="array">
						<item name="label" xsi:type="string" translate="true">Message</item>
						<item name="visible" xsi:type="boolean">true</item>
						<item name="dataType" xsi:type="string">text</item>
						<item name="formElement" xsi:type="string">textarea</item>
						<item name="dataScope" xsi:type="string">description</item>
						<item name="validation" xsi:type="array">
							<item name="required-entry" xsi:type="boolean">true</item>
						</item>
					</item>
				</argument>
			</field>
    </fieldset>
</form>

Create Save button

Create two files file Save.php and Generic.php with below content then put its in same folder app\code\Magerubik\Simple\Block\Adminhtml\Button

<?php
namespace Magerubik\Simple\Block\Adminhtml\Button;
use Magento\Backend\Block\Widget\Context;
use Magento\Cms\Api\PageRepositoryInterface;
class Generic
{
    protected $context;
    protected $pageRepository;
    public function __construct(
        Context $context,
        PageRepositoryInterface $pageRepository
    ) {
        $this->context = $context;
        $this->pageRepository = $pageRepository;
    }
    public function getUrl($route = '', $params = [])
    {
        return $this->context->getUrlBuilder()->getUrl($route, $params);
    }
}
<?php
namespace Magerubik\Simple\Block\Adminhtml\Button;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
use Magento\Ui\Component\Control\Container;
class Save extends Generic implements ButtonProviderInterface
{
    public function getButtonData()
    {
        return [
            'label' => __('Save'),
            'class' => 'save primary',
            'data_attribute' => [
                'mage-init' => [
                    'buttonAdapter' => [
                        'actions' => [
                            [
                                'targetName' => 'message_send_form.message_send_form',
                                'actionName' => 'save',
                                'params' => [
                                    false,
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ];
    }

}

4. Create model Data Provider

Create file app\code\Magerubik\Simple\Model\DataProvider\MessageDataProvider.php with below content.

<?php
namespace Magerubik\Simple\Model\DataProvider;
use Magerubik\Simple\Model\ResourceModel\Message\CollectionFactory;
use Magerubik\Simple\Model\ImageProcessor;
use Magento\Ui\DataProvider\AbstractDataProvider;
use Magento\Framework\App\Request\DataPersistorInterface;
class MessageDataProvider extends AbstractDataProvider
{
    protected $loadedData;
	private $imageProcessor;
    public function __construct(
        $name,
        $primaryFieldName,
        $requestFieldName,
        CollectionFactory $collectionFactory,
		ImageProcessor $imageProcessor,
		DataPersistorInterface $dataPersistor,
        array $meta = [],
        array $data = []
    ) {
        $this->collection = $collectionFactory->create();
		$this->imageProcessor = $imageProcessor;
		$this->dataPersistor = $dataPersistor;
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
    }
    public function getData()
    {
        $items = $this->collection->getItems();
        foreach ($items as $model) {
            $this->loadedData[$model->getId()] = $model->getData();
        }
		$data = $this->dataPersistor->get('mrsimple_message');
        if (!empty($data)) {
            $model = $this->collection->getNewEmptyItem();
            $model->setData($data);
            $this->loadedData[$model->getId()] = $model->getData();
            $this->dataPersistor->clear('mrsimple_message');
        }
        return $this->loadedData;
    }
}

Now, go to backend flush cache then check result. If you can see like below screenshot everything is ok.

5. Create controller save data

Create file app\code\Magerubik\Simple\Controller\Adminhtml\Message\save.php with below content.

<?php
namespace Magerubik\Simple\Controller\Adminhtml\Message;
use Magento\Framework\Exception\LocalizedException;
class Save extends \Magento\Backend\App\Action
{
    protected $dataPersistor;
    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\App\Request\DataPersistorInterface $dataPersistor
    ) {
        $this->dataPersistor = $dataPersistor;
        parent::__construct($context);
    }
    public function execute()
    {
        $resultRedirect = $this->resultRedirectFactory->create();
        $data = $this->getRequest()->getPostValue();
        if ($data) {
            $id = $this->getRequest()->getParam('messages_id');
			if($id){
				$model = $this->_objectManager->create(\Magerubik\Simple\Model\Message::class)->load($id);
				if (!$model->getMessagesId()) {
					$this->messageManager->addErrorMessage(__('This message no longer exists.'));
					return $resultRedirect->setPath('*/*/');
				}
				$model->setData($data)->setId($id);
			}else{
				$model = $this->_objectManager->create(\Magerubik\Simple\Model\Message::class);
				$model->setData($data)->setId(NULL);
			}
            try {
                $model->save();
                $this->messageManager->addSuccessMessage(__('You saved the message.'));
                $this->dataPersistor->clear('mrsimple_message');
                return $resultRedirect->setPath('*/*/edit', ['id' => $model->getMessagesId()]);
            } catch (LocalizedException $e) {
                $this->messageManager->addErrorMessage($e->getMessage());
            } catch (\Exception $e) {
                $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the message.'));
            }
            $this->dataPersistor->set('mrsimple_message', $data);
            return $resultRedirect->setPath('*/*/edit', ['id' => $this->getRequest()->getParam('messages_id')]);
        }
        return $resultRedirect->setPath('*/*/');
    }
}

Go to backend flush cache then check save function if see below the screenshot everything is ok.

magento 2 ui form check save function

Note: let’s get the user id from the customer table because we use it to get the user name in the grid.

OK, we’re done creating a simple UI from good luck with your practice. In the next post we will come back to this problem to add an image upload field. Contact us if you face any problems during the installation process.

You can download the demo code for this entire series from GitHub