Autocomplete textfield displays value when selected and not label I have a textfield autocomplete field that shows a list of full user names and email addresses as designed but when a user is selected, the user id is displayed in the field; not the name and email address.  This is the form element:

$form['people'] = [
  '#type' =&gt; 'textfield',
  '#placeholder' =&gt; t('Search by name'),
  '#autocomplete_route_name' =&gt; 'my_module.people',
];


This is the autocomplete handler in the controller:

public function handleAutocomplete(Request $request) {

  $query = $this-&gt;database-&gt;select('simplenews_subscriber', 'ss');
  $or = $query-&gt;orConditionGroup()
    -&gt;condition('pa.address_given_name', '%' .$request-&gt;query-&gt;get('q') . '%', 'LIKE')
    -&gt;condition('pa.address_family_name', '%' .$request-&gt;query-&gt;get('q') . '%', 'LIKE');
  $query-&gt;leftJoin('simplenews_subscriber__subscriptions', 'sss', 'sss.entity_id = ss.id');
  $query-&gt;leftJoin('profile', 'p', 'p.uid = ss.uid');
  $query-&gt;leftJoin('profile__address', 'pa', 'pa.entity_id = p.profile_id');
  $query-&gt;fields('ss', ['uid', 'mail']);
  $query-&gt;fields('pa', ['address_given_name', 'address_family_name']);
  $query-&gt;condition($or);
  $query-&gt;orderBy('address_given_name');
  $query-&gt;range(0, 10);

  $results = $query-&gt;execute()-&gt;fetchAll();

  $users = [];

  foreach ($results as $user) {
    $users[] = [
      'value' =&gt; $user-&gt;uid,
      'label' =&gt; $user-&gt;address_given_name . ' ' . $user-&gt;address_family_name . ' (' . $user-&gt;mail . ')',
    ];
  }

  return new JsonResponse($users);
}


The autocomplete textfield populates properly.  I just want the first name, last name and email address to show after a selection has been made.
To get this working I had to create a hidden field to store the value and use a custom selectHandler applied via class.

JavaScript File

(function ($, Drupal) {

  'use strict';

  /**
   * Override drupal selectHandler function
   */
  function customSelectHandler(event, ui) {
    var valueField = $(event.target);
    if($(event.target).hasClass('MYCUSTOM-autocomplete')) {
      var valueFieldName = event.target.name+'_value';
      if($('input[name='+valueFieldName+']').length &gt; 0) {
        valueField = $('input[name='+valueFieldName+']');
        // update the labels too
        const labels = Drupal.autocomplete.splitValues(event.target.value);
        labels.pop();
        labels.push(ui.item.label);
        event.target.value = labels.join(', ');
      }
    }
    const terms = Drupal.autocomplete.splitValues(valueField.val());
    // Remove the current input.
    terms.pop();
    // Add the selected item.
    terms.push(ui.item.value);

    valueField.val(terms.join(', '));
    // Return false to tell jQuery UI that we've filled in the value already.
    return false;
  }

  Drupal.behaviors.myCustomAutocomplete = {
    attach: function(context, settings) {
      // attach custom select handler to fields with class
      $('input.MYCUSTOM-autocomplete').autocomplete({
        select: customSelectHandler,
      });
    }
  };

})(jQuery, Drupal);


Form PHP

$form['my_field'] = array(
  '#type' =&gt; 'textfield',
  '#attributes' =&gt; ['class' =&gt; ['MYCUSTOM-autocomplete'],],
  '#autocomplete_route_name' =&gt; 'mymodule.myfield_autocomplete',
);
// field value
$form['my_field_value'] = array(
  '#type' =&gt; 'hidden',
);


On our forms we also have an AJAX callback on the autocompleteselect event, and I've noticed the value for the hidden field is blank but the userInput on the $form_state has the value.

Problem with this is the splitValues function may not work well for the labels, since it appears to be comma-delimited. You may want to write your own to handle it. It also won't work well if using the same form twice on a page, but there may be a way to narrow down the selector on the hidden field.