diff --git a/src/Plugin/Field/FieldWidget/FieldCollectionEmbedWidget.php b/src/Plugin/Field/FieldWidget/FieldCollectionEmbedWidget.php index 09c3c8f..bd1e9ff 100644 --- a/src/Plugin/Field/FieldWidget/FieldCollectionEmbedWidget.php +++ b/src/Plugin/Field/FieldWidget/FieldCollectionEmbedWidget.php @@ -7,6 +7,7 @@ namespace Drupal\field_collection\Plugin\Field\FieldWidget; +use Drupal\Component\Utility\Html; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\Field\FieldItemListInterface; @@ -34,20 +35,6 @@ class FieldCollectionEmbedWidget extends WidgetBase { /** * {@inheritdoc} */ - public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) { - $ret = parent::form($items, $form, $form_state, $get_delta); - $field_name = $this->fieldDefinition->getName(); - - // Add a new wrapper around all the elements for Ajax replacement. - $ret['#prefix'] = '
'; - $ret['#suffix'] = '
'; - - return $ret; - } - - /** - * {@inheritdoc} - */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { // TODO: Detect recursion $field_name = $this->fieldDefinition->getName(); @@ -109,7 +96,6 @@ class FieldCollectionEmbedWidget extends WidgetBase { // Put the remove button on unlimited cardinality field collection fields. if ($this->fieldDefinition->getFieldStorageDefinition()->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) { $options = ['query' => ['element_parents' => implode('/', $element['#parents'])]]; - $element['actions'] = [ '#type' => 'actions', 'remove_button' => [ @@ -124,7 +110,7 @@ class FieldCollectionEmbedWidget extends WidgetBase { 'callback' => [$this, 'ajaxRemove'], 'options' => $options, 'effect' => 'fade', - 'wrapper' => $field_name . '-ajax-wrapper', + 'wrapper' => $form['#wrapper_id'], ], '#weight' => 1000, ], @@ -135,6 +121,20 @@ class FieldCollectionEmbedWidget extends WidgetBase { } /** + * {@inheritdoc} + */ + protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) { + // Adjust wrapper identifiers as they are shared between parents and + // children in nested field collections. + $form['#wrapper_id'] = Html::getUniqueID($items->getName()); + $elements = parent::formMultipleElements($items, $form, $form_state); + $elements['#prefix'] = '
'; + $elements['#suffix'] = '
'; + $elements['add_more']['#ajax']['wrapper'] = $form['#wrapper_id']; + return $elements; + } + + /** * #after_build of a field collection element. * * Delays the validation of #required. @@ -252,6 +252,7 @@ class FieldCollectionEmbedWidget extends WidgetBase { // Where in the form we'll find the parent element. $address = array_slice($button['#array_parents'], 0, -4); + $address_state = array_slice($button['#parents'], 0, -3); // Go one level up in the form, to the widgets container. $parent_element = NestedArray::getValue($form, array_merge($address, array('widget'))); @@ -265,13 +266,11 @@ class FieldCollectionEmbedWidget extends WidgetBase { // item down one. This will overwrite the item being removed. for ($i = $delta; $i <= $field_state['items_count']; $i++) { $old_element_address = array_merge($address, array('widget', $i + 1)); - $old_element_state_address = array_merge($address, array($i + 1)); - $new_element_state_address = array_merge($address, array($i)); + $old_element_state_address = array_merge($address_state, array($i + 1)); + $new_element_state_address = array_merge($address_state, array($i)); $moving_element = NestedArray::getValue($form, $old_element_address); - $moving_element_value = NestedArray::getValue($form_state->getValues(), $old_element_state_address); - $moving_element_input = NestedArray::getValue($form_state->getUserInput(), $old_element_state_address); // Tell the element where it's being moved to. @@ -342,25 +341,13 @@ class FieldCollectionEmbedWidget extends WidgetBase { * * @return \Drupal\Core\Ajax\AjaxResponse * An AjaxResponse object. + * @see $this::removeSubmit() */ function ajaxRemove(array $form, FormStateInterface &$form_state) { - // Process user input. $form and $form_state are modified in the process. - //\Drupal::formBuilder()->processForm($form['#form_id'], $form, $form_state); - - // Retrieve the element to be rendered. - $trigger = $form_state->getTriggeringElement(); - $form_parents = explode('/', $trigger['#ajax']['options']['query']['element_parents']); - $address = array_slice($form_parents, 0, -1); - $form = NestedArray::getValue($form, $address); - $status_messages = array('#theme' => 'status_messages'); - - $renderer = \Drupal::service('renderer'); - $form['#prefix'] = empty($form['#prefix']) ? $renderer->render($status_messages) : $form['#prefix'] . $renderer->render($status_messages); - - $output = $renderer->render($form); - // TODO: Preserve javascript. See https://www.drupal.org/node/2502743 . - $response = new AjaxResponse(); - return $response->addCommand(new ReplaceCommand(NULL, $output)); + // At this point, $this->removeSubmit() removed the element so we just need + // to return the parent element. + $button = $form_state->getTriggeringElement(); + return NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -3)); } }