How to set up UI select in Magento 2 with values loaded on demand?
When one of our clients asked us to create a form related to folder management, I started looking for a solution that would allow efficient interaction with the select element that contains thousands of options. Displaying them all would be bad both from the system efficiency and admin’s quality of life point of view. UI select in Magento 2 has a feature that allows to search and load results asynchronously. Unfortunately, that feature isn’t very well documented. This short guide will show you how to set up UI select in Magento 2 to make it useful in our case.
UI component
Let’s assume that you’re working on a form as a UI component and you’re about to configure a select field that will list all products as available options. This is the part I will focus on in this post. You can read more about things related to UI components in our article series about custom product grid.
app/code/Magently/Backend/view/adminhtml/ui_component/my_form.xml:
<field name="my_select_field" formElement="select">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="label" xsi:type="string" translate="true">My select field</item>
<item name="isRemoveSelectedIcon" xsi:type="boolean">false</item>
<item name="componentType" xsi:type="string">field</item>
<item name="formElement" xsi:type="string">select</item>
<item name="component" xsi:type="string">Magento_Ui/js/form/element/ui-select</item>
<item name="elementTmpl" xsi:type="string">ui/grid/filters/elements/ui-select</item>
<item name="dataScope" xsi:type="string">data.my_select_field</item>
<item name="filterOptions" xsi:type="boolean">true</item> <!-- 1 -->
<item name="chipsEnabled" xsi:type="boolean">false</item>
<item name="disableLabel" xsi:type="boolean">true</item>
<item name="multiple" xsi:type="boolean">false</item>
<item name="required" xsi:type="boolean">false</item>
<item name="searchOptions" xsi:type="boolean">true</item> <!-- 2 -->
<item name="searchUrl" xsi:type="url" path="catalog/product/search"/> <!-- 3 -->
<item name="emptyOptionsHtml" xsi:type="string" translate="true">Start typing to find products...</item> <!-- 4 -->
<item name="filterRateLimit" xsi:type="number">1000</item> <!-- 5 -->
</item>
</argument>
</field>
I marked the lines key for asynchronous search with numbers in the comments.
filterOptions setting allows searching for a string in available options. If you enable only this functionality, you will be able to search in the data loaded synchronously before the form is rendered. Let’s also enable searchOptions (2) to load the filtered data asynchronously. Data collection requests will go to a URL indicated in searchURL (3). The emptyOptionsHtml field (4) will serve the admin as a brief label.
The final setting is filterRateLimit (5). I recommend setting it to 1000ms. I’ve been able to determine that if you leave this field’s default value, it often results in glitches during sending requests to the controller.
Controller
Depending on your needs, you will probably want to narrow down the number of results in UI select by applying extra filters (e.g. category, product type, etc.). To do this, you can modify the value of searchURL and create your own controller:
app/code/Magently/Backend/Controller/Adminhtml/Product/Search.php:
<?php
namespace Magently\Backend\Controller\Adminhtml\Product;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Framework\App\Action\HttpGetActionInterface;
/**
* Controller to search product for ui-select component
*/
class Search extends \Magento\Backend\App\Action implements HttpGetActionInterface
{
/**
* Authorization level of a basic admin session
*
* @see _isAllowed()
*/
const ADMIN_RESOURCE = 'Magento_Catalog::products';
/**
* @var \Magento\Framework\Controller\Result\JsonFactory
*/
private $resultJsonFactory;
/**
* @var \Magento\Catalog\Model\ProductLink\Search
*/
private $productSearch;
/**
* @param \Magento\Framework\Controller\Result\JsonFactory $resultFactory
* @param \Magento\Catalog\Model\ProductLink\Search $productSearch
* @param \Magento\Backend\App\Action\Context $context
*/
public function __construct(
\Magento\Framework\Controller\Result\JsonFactory $resultFactory,
\Magento\Catalog\Model\ProductLink\Search $productSearch,
\Magento\Backend\App\Action\Context $context
) {
$this->resultJsonFactory = $resultFactory;
$this->productSearch = $productSearch;
parent::__construct($context);
}
/**
* Execute product search.
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute() : \Magento\Framework\Controller\ResultInterface
{
$searchKey = $this->getRequest()->getParam('searchKey');
$pageNum = (int)$this->getRequest()->getParam('page');
$limit = (int)$this->getRequest()->getParam('limit');
/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection */
$productCollection = $this->productSearch->prepareCollection($searchKey, $pageNum, $limit);
/**
* ~~~~~~
* Apply additional conditions to $productCollection here
* ~~~~~~
*/
$totalValues = $productCollection->getSize();
$productById = [];
/** @var ProductInterface $product */
foreach ($productCollection as $product) {
$productId = $product->getId();
$productById[$productId] = [
'value' => $productId,
'label' => $product->getName(),
'is_active' => $product->getStatus(),
'path' => $product->getSku(),
'optgroup' => false
];
}
/** @var \Magento\Framework\Controller\Result\Json $resultJson */
$resultJson = $this->resultJsonFactory->create();
return $resultJson->setData([
'options' => $productById,
'total' => empty($productById) ? 0 : $totalValues
]);
}
}
UI select in Magento 2 – Summary
That’s it! I hope that this short guide will help you understand UI select in Magento better and make your life easier! If you have any questions, write them in the comments.