0

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:

  1. First search: Options load correctly, and I can select one without any problems.

  2. 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"

  3. 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 Vendor\Module\Controller\Adminhtml\OrderList;
use Magento\Backend\App\Action;
use Magento\Framework\Controller\Result\JsonFactory;
use Vendor\Module\Model\ResourceModel\OrderList\CollectionFactory as OrderListCollectionFactory;
class Search extends Action
{
 protected $orderListCollectionFactory;
 protected $jsonFactory;
 public function __construct(
 Action\Context $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?

asked Mar 6 at 22:13

1 Answer 1

-1

Solution: Modify the fetchData method to preserve the selected option even when new search results arrive.

Updated fetchData Method

Modify fetchData so that it retains the selected option:

fetchData(searchTerm) { if (!this.dataUrl) return;

this.isLoading(true);
let selectedOption = this.getSelected(); // Store current selection
$.ajax({
 url: this.dataUrl,
 method: 'GET',
 dataType: 'json',
 data: { searchTerm }
})
.done(response => {
 if (Array.isArray(response)) {
 let newOptions = response;
 // Ensure the currently selected option is retained
 if (selectedOption.length && !newOptions.find(opt => opt.value == selectedOption[0].value)) {
 newOptions.unshift(selectedOption[0]); // Add selected option back to the list
 }
 this.options([]); // Clear previous options
 this.options(newOptions); // 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));

}

Explanation of Fix

Before making an AJAX call, store the currently selected option. After fetching the new options, check if the selected value still exists in this.options(). If the selected value is missing, add it back to the list so getSelected() can find it.

Additional Fix: Ensure Label is Set Correctly Update the loadInitialLabel method to ensure the selected value and label are properly loaded:

loadInitialLabel(listId) { let self = this;

$.ajax({
 url: this.dataUrl,
 method: 'GET',
 dataType: 'json',
 data: { listId }
}).done(response => {
 if (response && response.label) {
 let existingOptions = self.options();
 
 // Check if the selected value is already in options
 if (!existingOptions.find(opt => opt.value == listId)) {
 existingOptions.unshift({ value: listId, label: response.label });
 }
 self.options(existingOptions);
 self.value(listId);
 }
}).fail(xhr => console.error('loadInitialLabel error:', xhr));

}

Expected Behavior After Fix ✔ After searching again, the selected option will remain available in the dropdown. ✔ The correct label will be displayed instead of "Entity with ID ... doesn't exist". ✔ The UI-Select component will retain the selected value without issues.

answered Mar 7 at 7:44
1
  • Thanks for your response. However, your answer appears to be AI-generated. While it provides general guidance, it doesn't directly address why the label disappears after a new search or how getSelected() behaves in this scenario. If you have personally tested this solution or have deeper insights, I’d appreciate a more detailed explanation. Otherwise, a more specific answer would be helpful. Thanks! Commented Mar 7 at 18:09

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.