I am using a ui-select
field in my Magento 2 admin form. The dropdown fetches data via AJAX on search and correctly displays options. However, I’m facing an issue:
-
First search: Options load correctly, and I can select one without any problems.
-
After clearing the search and searching again: The new options appear correctly, but when I select an option, the field displays: “Entity with ID: … doesn’t exist”
-
After this issue appears, any further selection also shows the same message, instead of displaying the correct label.
There are no console errors. The selected value is correctly saved and retrieved, but the label does not display properly.
Upon debugging, I found that the issue originates from Magento_Ui/js/form/element/ui-select
, particularly in the setCaption
function:
setCaption: function () {
var length, caption = '';
if (!_.isArray(this.value()) && this.value()) {
length = 1;
} else if (this.value()) {
length = this.value().length;
} else {
this.value([]);
length = 0;
}
this.warn(caption);
// Check if the option was removed
if (this.isDisplayMissingValuePlaceholder && length && !this.getSelected().length) {
caption = this.missingValuePlaceholder.replace('%s', this.value());
this.placeholder(caption);
this.warn(caption);
return this.placeholder();
}
if (length > 1) {
this.placeholder(length + ' ' + this.selectedPlaceholders.lotPlaceholders);
} else if (length && this.getSelected().length) {
this.placeholder(this.getSelected()[0].label);
} else {
this.placeholder(this.selectedPlaceholders.defaultPlaceholder);
}
return this.placeholder();
},
What I have observed in the HTML
When the label displays correctly:
<div class="admin__action-multiselect-text" data-role="selected-option" data-bind="css: {warning: warn().length}, text: setCaption()">Five Guys OCS</div>
When the issue occurs:
<div class="admin__action-multiselect-text warning" data-role="selected-option" data-bind="css: {warning: warn().length}, text: setCaption()">Entity with ID: 13166 doesn't exist</div>
Configuration (UI Component XML)
<field name="list_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="label" xsi:type="string" translate="true">List Name</item>
<item name="componentType" xsi:type="string">field</item>
<item name="formElement" xsi:type="string">select</item>
<item name="component" xsi:type="string">Vendor_Module/js/form/element/orderlist-select</item>
<item name="elementTmpl" xsi:type="string">ui/grid/filters/elements/ui-select</item>
<item name="dataUrl" xsi:type="url" path="orderlists/orderlist/search"/>
<item name="searchUrlParameter" xsi:type="string">searchTerm</item>
<item name="hasSearchInput" xsi:type="boolean">true</item>
<item name="filterOptions" xsi:type="boolean">true</item>
<item name="lazyLoad" xsi:type="boolean">true</item>
<item name="selectType" xsi:type="string">single</item>
<item name="multiple" xsi:type="boolean">false</item>
<item name="required" xsi:type="boolean">true</item>
<item name="dataScope" xsi:type="string">list_id</item>
</item>
</argument>
</field>
JavaScript Component
define([
'Magento_Ui/js/form/element/ui-select',
'jquery',
'ko'
], function (UiSelect, $, ko) {
'use strict';
return UiSelect.extend({
defaults: {
isLoading: ko.observable(false),
options: ko.observableArray([]),
showFilteredQuantity: true
},
initialize() {
this._super();
this._initSearchListener();
// If there's an existing value, fetch its label
if (this.value()) {
this.loadInitialLabel(this.value());
}
return this;
},
_initSearchListener() {
this.filterInputValue.subscribe(searchTerm => {
if (searchTerm.length >= 1) {
this.fetchData(searchTerm);
}
});
},
fetchData(searchTerm) {
if (!this.dataUrl) return;
this.isLoading(true);
$.ajax({
url: this.dataUrl,
method: 'GET',
dataType: 'json',
data: { searchTerm }
})
.done(response => {
if (Array.isArray(response)) {
this.options([]); // Clear previous options
this.options(response); // Load new options
this.hasData(true);
this.trigger('optionsUpdated'); // Refresh UI
} else {
console.error('Invalid response format:', response);
}
})
.fail(xhr => console.error('fetchData error:', xhr))
.always(() => this.isLoading(false));
},
loadInitialLabel(listId) {
let self = this;
$.ajax({
url: this.dataUrl, // Use same endpoint to get the label
method: 'GET',
dataType: 'json',
data: { listId }
}).done(response => {
if (response && response.label) {
self.options([{ value: listId, label: response.label }]);
self.value(listId);
}
}).fail(xhr => console.error('loadInitialLabel error:', xhr));
},
getPath() {
return '';
}
});
});
Controller Handling AJAX Search
<?php
namespace VendorModuleControllerAdminhtmlOrderList;
use MagentoBackendAppAction;
use MagentoFrameworkControllerResultJsonFactory;
use VendorModuleModelResourceModelOrderListCollectionFactory as OrderListCollectionFactory;
class Search extends Action
{
protected $orderListCollectionFactory;
protected $jsonFactory;
public function __construct(
ActionContext $context,
OrderListCollectionFactory $orderListCollectionFactory,
JsonFactory $jsonFactory
) {
parent::__construct($context);
$this->orderListCollectionFactory = $orderListCollectionFactory;
$this->jsonFactory = $jsonFactory;
}
public function execute()
{
$searchTerm = trim($this->getRequest()->getParam('searchTerm', ''));
$listId = trim($this->getRequest()->getParam('listId', ''));
$collection = $this->orderListCollectionFactory->create();
if ($listId) {
$item = $collection->addFieldToFilter('list_id', $listId)->getFirstItem();
if ($item->getId()) {
return $this->jsonFactory->create()->setData([
'value' => (string) $item->getListId(),
'label' => $item->getName()
]);
}
return $this->jsonFactory->create()->setData([]);
}
if (!$searchTerm) {
return $this->jsonFactory->create()->setData([]);
}
$collection->addFieldToFilter('name', ['like' => "%{$searchTerm}%"])->setPageSize(50);
$options = [];
foreach ($collection as $item) {
$options[] = [
'value' => (string) $item->getListId(),
'label' => $item->getName()
];
}
return $this->jsonFactory->create()->setData($options);
}
}
Question
How can I ensure that the selected option always displays its correct label instead of "Entity with ID ... doesn't exist"
when searching again? What might be causing getSelected()
to return an empty value after a new search?