contacts_events-8.x-1.x-dev/src/Element/AjaxUpdate.php
src/Element/AjaxUpdate.php
<?php namespace Drupal\contacts_events\Element; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element\Submit; /** * Provides an element to handle AJAX updates. * * @FormElement("ajax_update") */ class AjaxUpdate extends Submit { /** * The render array for this element. * * @var array */ protected $element; /** * {@inheritdoc} */ public function getInfo() { return [ '#printed' => TRUE, '#updates' => [], '#validate' => [], '#submit' => [], '#limit_validation_errors' => [], ] + parent::getInfo(); } /** * Create an ajax update handler element. * * @return static * The element handler. */ public static function createElement() { return \Drupal::service('plugin.manager.element_info') ->createInstance('ajax_update'); } /** * Get the render array for the element. * * @param string $name * The #name to use. * @param string|null $unique_suffix * If $name may not be unique, provide a unique suffix. This must be * preserved between AJAX requests. * * @return array * The render array. The element handler is stored at #element. * * @throws \Exception * Thrown if a second attempt is made to retrieve the render array. */ public function &getRenderArray($name, $unique_suffix = NULL) { if (!isset($this->element)) { $this->element = [ '#element' => $this, '#type' => 'ajax_update', '#ajax' => [ 'event' => 'click', 'callback' => [static::class, 'ajaxCallback'], ], '#name' => $name . ($unique_suffix ? "[{$unique_suffix}]" : ''), '#unique_suffix' => $unique_suffix, ]; return $this->element; } else { throw new \Exception('Element already retrieved.'); } } /** * Register an element that be updated as part of this ajax update. * * @param array $element * The render element to update. * * @return $this */ public function registerElementToUpdate(array &$element) { // Track that this is the element we need to update, keyed by ID to prevent // duplicates. $id = static::getId($element); if (!$id) { throw new \InvalidArgumentException('Element must have an ID for AJAX updates.'); } $this->element['#updates'][$id] = &$element['#array_parents']; return $this; } /** * Register an element that should trigger this ajax update. * * @param array $element * The render element that triggers this. * @param array $options * An optional array of #ajax options. * @param array|false|null $parents * Optionally provide parents for limiting validation errors. NULL will use * $element['#parents'], if set, FALSE will prevent limiting and an array * will be added to the elements #limit_validation_errors array. * * @return $this */ public function registerElementToRespondTo(array &$element, array $options = [], $parents = NULL) { $element['#ajax'] = $options + [ 'callback' => $this->element['#ajax']['callback'], 'trigger_as' => [ 'name' => &$this->element['#name'], ], ]; if (isset($parents)) { if ($parents) { $this->element['#limit_validation_errors'][] = $parents; } } elseif (!empty($element['#parents'])) { $this->element['#limit_validation_errors'][] = &$element['#parents']; } return $this; } /** * Register a path to include in limit validation errors. * * @param array $parents * The parents array path to register for limited validation. */ public function registerLimitValidationErrors(array $parents) : void { $this->element['#limit_validation_errors'][] = $parents; } /** * Form AJAX callback to update the form. * * @param array $form * The form. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state. * * @return \Drupal\Core\Ajax\AjaxResponse * An AJAX response to update the form. */ public static function ajaxCallback(array $form, FormStateInterface $form_state) { $response = new AjaxResponse(); $triggering_element = $form_state->getTriggeringElement(); // For each of our targets, add a command to update it. foreach ($triggering_element['#updates'] as $array_parents) { $element = NestedArray::getValue($form, $array_parents); if ($id = static::getId($element)) { $response->addCommand(new ReplaceCommand('#' . $id, $element)); } } return $response; } /** * Get an ID for an element, using the unique suffix if there is one. * * @param string $id * The base ID to use. * * @return string * The ID with a unique suffix, if applicable. */ public function getIdWithUniqueSuffix($id) { if ($this->element['#unique_suffix']) { return "{$id}-{$this->element['#unique_suffix']}"; } else { return $id; } } /** * Get the wrapper ID for replacements. * * Uses the element if it is a container. Otherwise it uses or adds a * container wrapper. * * @param array $element * The element. * * @return string * The wrapper ID. */ protected static function getId(array &$element) { // Get hold of a the container to use. switch ($element['#type'] ?? NULL) { case 'container': $container = &$element; break; // In all other cases, wrap in a container. default: if (!isset($element['#theme_wrappers']['container'])) { $element['#theme_wrappers']['container'] = []; } $container = $element['#theme_wrappers']['container']; break; } // Return our ID. return $container['#id']; } }