Same name and namespace in other branches
  1. 8.9.x core/modules/views_ui/admin.inc \views_ui_add_ajax_trigger()
  2. 9 core/modules/views_ui/admin.inc \views_ui_add_ajax_trigger()

Converts a form element in the add view wizard to be AJAX-enabled.

This function takes a form element and adds AJAX behaviors to it such that changing it triggers another part of the form to update automatically. It also adds a submit button to the form that appears next to the triggering element and that duplicates its functionality for users who do not have JavaScript enabled (the button is automatically hidden for users who do have JavaScript).

To use this function, call it directly from your form builder function immediately after you have defined the form element that will serve as the JavaScript trigger. Calling it elsewhere (such as in hook_form_alter()) may mean that the non-JavaScript fallback button does not appear in the correct place in the form.

Parameters

$wrapping_element: The element whose child will server as the AJAX trigger. For example, if $form['some_wrapper']['triggering_element'] represents the element which will trigger the AJAX behavior, you would pass $form['some_wrapper'] for this parameter.

$trigger_key: The key within the wrapping element that identifies which of its children serves as the AJAX trigger. In the above example, you would pass 'triggering_element' for this parameter.

$refresh_parents: An array of parent keys that point to the part of the form that will be refreshed by AJAX. For example, if triggering the AJAX behavior should cause $form['dynamic_content']['section'] to be refreshed, you would pass array('dynamic_content', 'section') for this parameter.

4 calls to views_ui_add_ajax_trigger()
ViewAddForm::form in core/modules/views_ui/src/ViewAddForm.php
Gets the actual form array to be built.
WizardPluginBase::buildFilters in core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
Builds the form structure for selecting the view's filters.
WizardPluginBase::buildForm in core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
Form callback to build other elements in the "show" form.
WizardPluginBase::buildFormStyle in core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
Adds the style options to the wizard form.

File

core/modules/views_ui/admin.inc, line 43
Provides the Views' administrative interface.

Code

function views_ui_add_ajax_trigger(&$wrapping_element, $trigger_key, $refresh_parents) {
  $seen_ids =& drupal_static(__FUNCTION__ . ':seen_ids', []);
  $seen_buttons =& drupal_static(__FUNCTION__ . ':seen_buttons', []);

  // Add the AJAX behavior to the triggering element.
  $triggering_element =& $wrapping_element[$trigger_key];
  $triggering_element['#ajax']['callback'] = 'views_ui_ajax_update_form';

  // We do not use \Drupal\Component\Utility\Html::getUniqueId() to get an ID
  // for the AJAX wrapper, because it remembers IDs across AJAX requests (and
  // won't reuse them), but in our case we need to use the same ID from request
  // to request so that the wrapper can be recognized by the AJAX system and
  // its content can be dynamically updated. So instead, we will keep track of
  // duplicate IDs (within a single request) on our own, later in this function.
  $triggering_element['#ajax']['wrapper'] = 'edit-view-' . implode('-', $refresh_parents) . '-wrapper';

  // Add a submit button for users who do not have JavaScript enabled. It
  // should be displayed next to the triggering element on the form.
  $button_key = $trigger_key . '_trigger_update';
  $element_info = \Drupal::service('element_info');
  $wrapping_element[$button_key] = [
    '#type' => 'submit',
    // Hide this button when JavaScript is enabled.
    '#attributes' => [
      'class' => [
        'js-hide',
      ],
    ],
    '#submit' => [
      'views_ui_nojs_submit',
    ],
    // Add a process function to limit this button's validation errors to the
    // triggering element only. We have to do this in #process since until the
    // form API has added the #parents property to the triggering element for
    // us, we don't have any (easy) way to find out where its submitted values
    // will eventually appear in $form_state->getValues().
    '#process' => array_merge([
      'views_ui_add_limited_validation',
    ], $element_info
      ->getInfoProperty('submit', '#process', [])),
    // Add an after-build function that inserts a wrapper around the region of
    // the form that needs to be refreshed by AJAX (so that the AJAX system can
    // detect and dynamically update it). This is done in #after_build because
    // it's a convenient place where we have automatic access to the complete
    // form array, but also to minimize the chance that the HTML we add will
    // get clobbered by code that runs after we have added it.
    '#after_build' => array_merge($element_info
      ->getInfoProperty('submit', '#after_build', []), [
      'views_ui_add_ajax_wrapper',
    ]),
  ];

  // Copy #weight and #access from the triggering element to the button, so
  // that the two elements will be displayed together.
  foreach ([
    '#weight',
    '#access',
  ] as $property) {
    if (isset($triggering_element[$property])) {
      $wrapping_element[$button_key][$property] = $triggering_element[$property];
    }
  }

  // For easiest integration with the form API and the testing framework, we
  // always give the button a unique #value, rather than playing around with
  // #name. We also cast the #title to string as we will use it as an array
  // key and it may be a TranslatableMarkup.
  $button_title = !empty($triggering_element['#title']) ? (string) $triggering_element['#title'] : $trigger_key;
  if (empty($seen_buttons[$button_title])) {
    $wrapping_element[$button_key]['#value'] = t('Update "@title" choice', [
      '@title' => $button_title,
    ]);
    $seen_buttons[$button_title] = 1;
  }
  else {
    $wrapping_element[$button_key]['#value'] = t('Update "@title" choice (@number)', [
      '@title' => $button_title,
      '@number' => ++$seen_buttons[$button_title],
    ]);
  }

  // Attach custom data to the triggering element and submit button, so we can
  // use it in both the process function and AJAX callback.
  $ajax_data = [
    'wrapper' => $triggering_element['#ajax']['wrapper'],
    'trigger_key' => $trigger_key,
    'refresh_parents' => $refresh_parents,
  ];
  $seen_ids[$triggering_element['#ajax']['wrapper']] = TRUE;
  $triggering_element['#views_ui_ajax_data'] = $ajax_data;
  $wrapping_element[$button_key]['#views_ui_ajax_data'] = $ajax_data;
}