cancel
Showing results for 
Search instead for 
Did you mean: 

Magento 2 UI-Select: "Entity with ID ... doesn't exist" After Searching Again

Magento 2 UI-Select: "Entity with ID ... doesn't exist" After Searching Again

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?

1 REPLY 1

Re: Magento 2 UI-Select: "Entity with ID ... doesn't exist" After Searching Again

Hello @Partab Saif,

 

The issue you're facing is likely due to the fact that the options array is being overwritten when new search results are loaded, which causes the previously selected option to be lost if it's not part of the new search results. This results in the getSelected() method returning an empty array, leading to the "Entity with ID ... doesn't exist" error.

Why This Happens:

  1. When a new search is performed, the options([]) line clears all previous options.
  2. If the currently selected value is not part of the new results, it cannot match a label to the value.
  3. The setCaption method then fails to find the selected option's label, triggering the error.

Solution: Preserve Selected Option:

To fix this issue, you need to preserve the currently selected option when fetching new options. You can achieve this by checking if the current value is still valid and merging it with the new search results.

Fix in the JavaScript Component:

Update the fetchData method to retain the currently selected option:

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

this.isLoading(true);
const currentValue = this.value();

$.ajax({
url: this.dataUrl,
method: 'GET',
dataType: 'json',
data: { searchTerm }
})
.done(response => {
if (Array.isArray(response)) {
const updatedOptions = [];

// If there is a selected value, ensure it remains in the options
if (currentValue) {
const existingOption = this.getSelected()[0];
if (existingOption) {
updatedOptions.push(existingOption);
} else {
// Attempt to load the label for the current value if missing
this.loadInitialLabel(currentValue);
}
}

// Add new search results to the options
updatedOptions.push(...response);

this.options(updatedOptions); // 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));
},

Changes Made:

  1. Preserve Selected Value: Before clearing the options, it checks if there's a currently selected value (currentValue).
  2. Keep Selected Option: If the selected option exists (this.getSelected()[0]), it adds it to the updatedOptions array.
  3. Load Label if Missing: If the selected value is not found, it calls loadInitialLabel() to fetch the label from the server.
  4. Merge New Options: Combines the preserved option with the new search results.

Additional Tip: Improve loadInitialLabel Method:

Make sure loadInitialLabel is called only when needed and is able to update the options properly:

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) {
const existingOptions = self.options();
existingOptions.push({ value: listId, label: response.label });
self.options(existingOptions); // Preserve previous options
self.value(listId);
}
}).fail(xhr => console.error('loadInitialLabel error:', xhr));
}

By retaining the selected option and merging it with new search results, the UI component will always display the correct label and avoid the "Entity with ID ... doesn't exist" error. This approach ensures a smoother user experience while dynamically fetching options.