HowTo: Use the Form Ui Component in Magento 2


The Form Ui Component is what Magento uses everywhere in the admin to render edit screens. The first time you build one it feels like a lot of XML for a simple form, but the pattern is always the same, so once you have done it once you can do it in your sleep.

A form needs two things: an XML file that describes the fields, and a data provider that feeds it the current values.

This post assumes that there is already a custom module created in app/code/Vendor/Module with an entity model and its resource model. Here we focus on the form itself.

The form XML

xml
<?xml version="1.0"?>
<!-- app/code/Vendor/Module/view/adminhtml/ui_component/vendor_entity_form.xml -->
<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">vendor_entity_form.vendor_entity_form_data_source</item>
        </item>
    </argument>
    <settings>
        <namespace>vendor_entity_form</namespace>
        <dataScope>data</dataScope>
    </settings>
    <dataSource name="vendor_entity_form_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Vendor\Module\Model\Entity\DataProvider</argument>
            <argument name="name" xsi:type="string">vendor_entity_form_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
            <argument name="requestFieldName" xsi:type="string">id</argument>
        </argument>
    </dataSource>
    <fieldset name="general">
        <settings>
            <label translate="true">General</label>
        </settings>
        <field name="title" formElement="input">
            <settings>
                <label translate="true">Title</label>
                <dataType>text</dataType>
            </settings>
        </field>
    </fieldset>
</form>

The data provider

The data provider extends AbstractDataProvider (or the modern Magento\Ui\DataProvider\AbstractDataProvider) and its job is to return the data of the record being edited, keyed by its id.

php
<?php
/** app/code/Vendor/Module/Model/Entity/DataProvider.php */
namespace Vendor\Module\Model\Entity;

use Magento\Ui\DataProvider\AbstractDataProvider;
use Vendor\Module\Model\ResourceModel\Entity\CollectionFactory;

class DataProvider extends AbstractDataProvider
{
    /**
     * @var array
     */
    private $loadedData;

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

    /**
     * @return array
     */
    public function getData()
    {
        if ($this->loadedData !== null) {
            return $this->loadedData;
        }

        $this->loadedData = [];
        foreach ($this->collection->getItems() as $entity) {
            $this->loadedData[$entity->getId()] = $entity->getData();
        }

        return $this->loadedData;
    }
}

That is the whole loop: the form asks the data source for data, the data source is our provider, and the provider hands back the record indexed by its id so each field knows its current value. Wire a controller that renders this form, deploy the admin static content, flush the cache and you have a working edit screen.

Hope this helps.

If you have any question or know a better way to achieve this, feel free to share in the comments below.

Related links: