Score:1

How to filter autocomplete on a dependent field?

km flag

Goal - How to filter autocomplete based on a related field? In this domain, an organisation is selected and then the autocomplete should only allow items created by that organisation to be selected.

The setup involves an

  • inline entity form to select the organisation. This is the triggering field.
  • auto complete field to select a related bond by that organisation

The triggering element is called 'issuer'. This is the form structure:

Issuer IEF form structure

The autocomplete field structure in the form is:

Deal Bond form structure

Forgetting about how the ajax is triggered when the inline entity form completes as another problem. I have a system where a fake/triggering select list is changed based on a mutation observer triggering the ajax. I have investigated trying to chain the event, trying to apply the ajax to the inline entity form element and trying to call the ajax using drupal.ajax.

How do I set the filter value id. I can see 2 ways, the cleanest is passing a #filter parameter to #selection_settings

$form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#selection_settings'] = [
  'target_bundles' => ['bean'],
  'filter' => ['field_bond_issuer' => $issuer_id],
];

I can see the value set in a #prefix element on the form after the ajax has run

$form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#prefix'] = 'testing add content: issuer id' . $issuer_id;
return $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond'];

The field appearance:

enter image description here

The html

<input data-drupal-selector="edit-field-para-deal-information-0-subform-field-deal-reference-bond-0-target-id" aria-describedby="edit-field-para-deal-information-0-subform-field-deal-reference-bond-0-target-id--_drZ4Yf-StI--description" class="form-autocomplete form-text ui-autocomplete-input" data-autocomplete-path="/entity_reference_autocomplete/node/default%3Anode_by_issuer/tT7cuAtHgjMlRrK6OypcvFEJNTvFEDSmFImi6iBhNNw" type="text" id="edit-field-para-deal-information-0-subform-field-deal-reference-bond-0-target-id--_drZ4Yf-StI" name="field_para_deal_information[0][subform][field_deal_reference_bond][0][target_id]" value="" size="60" maxlength="1024" placeholder="" autocomplete="off">

Shows that default:node_by_issuer is set in the ajax function (full):

function cbi_deal_id_handle_ajax(array &$form, FormStateInterface $form_state) {
  $issuer_para_field = $form_state->getValue('field_para_issuer_information');
  $issuer_field = $issuer_para_field[0]['subform']['field_bond_issuer'];
  $issuer_field_value = $issuer_field['target_id'];
  $issuer_id = explode(':', $issuer_field_value)[1];


  $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#selection_handler'] = 'default:node_by_issuer';
  $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#selection_settings'] = [
'target_bundles' => ['bean'],
'filter' => ['field_bond_issuer' => $issuer_id],
  ];

 $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#prefix'] = 'testing add content: issuer id' . $issuer_id;
 return $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond'];
 }

I break at Review at /web/core/lib/Drupal/Core/Render/Element/FormElement.php line 187 and confirm the filter variable is not set.

It appears that the autocomplete field is not being updated with settings relating to the autocomplete function. If I set the ajax to update the form with

['#selection_handler'] = 'default:node_by_tester';

there is no change to the handler in the core renderer. The value set by the initial form alter remains (node_by_issuer).

The ajax form alter, with wrapping div to test if the form wrapper id with the hash-code on the end makes any difference

function cbi_deal_id_field_widget_entity_reference_paragraphs_form_alter(&$element, &$form_state, $context) {
  if ($element['#paragraph_type'] == 'deal_information') {
    $items = $context['items'];
    $bond = $items->getParent()->getEntity();
    $element['subform']['field_deal_reference_bond']['widget'][0]['target_id']['#selection_handler'] = 'default:node_by_issuer';
    $element['subform']['field_deal_reference_bond']['#prefix'] = '<div id="test-wrapper">';
    $element['subform']['field_deal_reference_bond']['#suffix'] = '</div>';

    $element['subform']['ajax_trigger_button'] = [
      '#type' => 'select',
      '#ajax' => [
        'callback' => 'cbi_deal_id_handle_ajax',
        'event' => 'blur',
        'wrapper' => 'test-wrapper',
        'progress' => [
          'type' => 'throbber',
          'message' => t('Loading issuer details ...'),
         ],
      ],
     '#options' => [
      '1' => t('One'),
      '2' => t('Two'),
      '3' => t('Three'),
     ],
   ];

  if ($bond->isNew()) {
    return;
  }
...

In order to filter the data I've created a plugin as per https://fivejars.com/blog/when-drupal-best-choice-your-website. I'm expecting that the selection_handler property addresses it. This is being called, without access to the filter data:

<?php

namespace Drupal\cbi_deal_id\Plugin\EntityReferenceSelection;

use Drupal\node\Plugin\EntityReferenceSelection\NodeSelection;

/**
 * Provides specific access control for the node entity type.
 *
 * @EntityReferenceSelection(
 *   id = "default:node_by_issuer",
 *   label = @Translation("Node by field selection"),
 *   entity_types = {"node"},
 *   group = "default",
 *   weight = 3
 * )
 */
class BondByIssuerSelection extends NodeSelection {

 /**
  * {@inheritdoc}
  */
 protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
   \Drupal::logger('cbi_deal_id')->notice('loading build entity query with');
   $query = parent::buildEntityQuery($match, $match_operator);
   $handler_settings = $this->configuration['handler_settings'];
   if (!isset($handler_settings['filter'])) {
     return $query;
   }
   $filter_settings = $handler_settings['filter'];
   foreach ($filter_settings as $field_name => $value) {
     $query->condition($field_name, $value, '=');
   }
   return $query;
 }

}

Ajax returning the entire paragraph

Adjust the ajax to replace the entire paragraph as suggested

$form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['target_id']['#selection_settings'] = 
  [
     'target_bundles' => ['bond'],
     'filter' => ['field_issuer_filter' => 4993],
   ];
$form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#prefix'] = 'testing add content: issuer id' . $issuer_id;
  return $form['field_para_deal_information'];
}

In the matcher file /src/Entity/CbiDealIdEntityAutocompleteMatcher.php

The filter settings are in the autocomplete matcher controller, but not the updated values

enter image description here

Conclusion

I have tried creating a controller https://www.chapterthree.com/blog/how-alter-entity-autocomplete-results-drupal-8. It does get called, but I have no filter data and cannot see how to address the $form_state to get the id from the inline entity form.

The second method of addressing the query is via a custom route. Possibly can change the route from ajax. Investigation required.

What is the simplest method of filtering by the value in an inline entity field?

No Sssweat avatar
ua flag
In your ajax callback, you need to just `return $form['field_para_deal_information'];`
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.