Allow for deletion of a single value of a multiple value field

Created on 24 January 2011, over 13 years ago
Updated 5 June 2024, 21 days ago

Problem/Motivation

I'm working on creating a custom field that has multiple entry items in the field. The field would look like this
Field 1 - Text
Field 2 - Text
Field 3 - Text

All would be saved into separate columns.

Now my question is with a cardinality of -1 the Add More works great to put the next item to enter but how would one item be removed from the list of them. Ideally a "delete" or "remove" button would be at the end of the item line but I don't see this. I know with a single field emptying the text field "removes" it but this isn't obvious to a use.

It's not unlike the Remove button that appears next to the Image Fields when multiples are allowed.

Is this something available or less of a support request and more of a feature request?

Note: For 7.x there is a contrib module for that: https://www.drupal.org/project/multiple_fields_remove_button
This issue is about adding that functionality to core.

Copied from 🐛 Deleting the first value in a required, multivalue field fails validation Closed: duplicate

Proposed resolution

Use ajax and reload element to remove the item from the page.

Remaining tasks

  • Decide on direction
  • Add tests
  • Review
  • Commit

User interface changes

API changes

Data model changes

📌 Task
Status

Fixed

Version

11.0 🔥

Component
Field 

Last updated about 18 hours ago

Created by

Live updates comments and jobs are added and updated live.
  • Usability

    Makes Drupal easier to use. Preferred over UX, D7UX, etc.

Missing content requested by

🇦🇺Australia dpi
7 months ago
Sign in to follow issues

Merge Requests

Comments & Activities

  • 🇫🇷France yched

    Feature request :-)

  • That's sort of what I figured but wanted to check :)

    Similar request but for 6.x CCK
    http://drupal.org/node/196421

    The image really shows what would be ideal.
    http://drupal.org/files/issues/cck-196421-71.remove-button-example.png

  • 🇦🇺Australia danielb

    I support this, I have two modules that appear buggy in some configurations because there is no way to remove one of the multiple values added with 'add another item'.
    The modules are select_or_other and blockreference. I have given users, in addition to a regular multiselect box, the ability to create multiple drag-and-drop single select lists, which allows them to change the order of the selections.
    However when set to 'required' and 'unlimited', there is #1117916: No way to remove added options .
    The image shown in #2 by rogueturnip, showing a removal link on the right, is exactly what I've come here to request.

  • 🇩🇰Denmark LarsKramer Copenhagen

    Nice idea. Moving to Drupal 8 where feature requests belong.

    Attaching an image from Quick Tabs module which also has a remove button to the right of each instance.

  • I like that this is being added to 8.x as a Feature Request but Drupal 7 is pretty early in it's life and ideally this would also appear there too.

    I haven't tried this but I'm wondering if a kludge would be a button with javascript linked to it to empty the fields and then hide the row. Wouldn't degrade nicely but it's a thought.

  • 🇧🇷Brazil afeijo Porto Alegre

    I made that jQuery idea already, rogueturnip

    but if you hit the add another value button, the deleted one returns :p

    I will improve the jquery code to watch the button and delete the line again

  • What if instead of hiding the deleted row it instead greyed it out and changed the delete button to restore. This would allow for deleting rows and adding rows and then all the changes would be done on save. This way if you made a mistake on the delete you could restore it.

    Any chance you could share what you've done with jquery so far?

  • 🇺🇸United States tim.plunkett Philadelphia

    #8 is a second feature request on top of this one.

  • I guess it would be. Maybe more of an enhancement to the initial feature request.

    What I was looking for was if afeigo had any jquery code/module code he's written that he could share to get this feature in place without waiting for core changes.

  • Maybe someone with more jquery and multivalue form understanding could tell me if this might work.

    1. use js (through hook_form_alter?) to add a delete/restore button at the end of each element. use $form['#attached']?
    2. if deleting, set disabled=disabled (grey it out), if restoring remove disabled
    3. if deleting add a class of deleted
    4. use hook_form_alter to add a new submit function that clears all the values for the elements with class=deleted

    Because of my lack of deep understanding of mutlivalue elements I don't know if when a add item is clicked if any of this would even execute or if there would be some way to attach the action to the add item.

    My guess is this might be able to be done in a contributed module for Drupal 7.

  • 🇫🇷France yched

    There was a similar debate for the CCK D6 multigroup feature.
    What got implemented over there (done in parallel and independently of the D7 Field API work) was a "delete" button that let you "hide / fold" the corresponding row (or "unhide / unfold" it).

  • 🇬🇧United Kingdom Fidelix

    Subscribing... This is much needed.

  • 🇵🇱Poland Refineo

    subscribing

  • 🇬🇧United Kingdom jibize

    Yes Sub!

  • 🇳🇴Norway odegard

    I've solved this in probably a very unsexy manner, but it works.

    I've got a multiple field in a content type called TYPE. The field is called 'field_extrafield' and is a term reference field in my case.

    Create a module called HOOK with this function:

    function HOOK_node_validate($node, $form, &$form_state) {
      if ($node->form_id == 'TYPE_node_form') {
        foreach ($node->field_extrafield['und'] as $mkey => $mvalue) {
          if (is_array($mvalue)) {
            if (!isset($mvalue['tid'])) {
              form_set_value($form['field_extrafield']['und'][$mkey], NULL, $form_state);
            }
          }
        }
      }
    }
    

    This might not work out of the box. Mu case inparticular involves field collections so the arrays look different, but this should get you going.

    This function add custom validation to the form after it is submitted. The first IF assures you you're working on the correct form. Then we're checking if a value has been set in the multiple field. The fields will be listed as numbered arrays under ['field_name']['und'], but there will also be string items here, so you need to check that you're working on items that are arrays (is_array). Since this is a term ref in my case, I'm checking to see if the array under ['und'] has the ['tid'] set. If not, I set the whole array to NULL using the form_set_value function. This is the only way to write changes to the form inside the validation function.

    This feels like a very brutal way of doing it... setting fields to NULL in the validation form... any other suggestions?

  • 🇧🇪Belgium rp7

    subscribe

  • 🇳🇴Norway odegard

    1) It would probably be better to unset the field rather than set it to NULL.

    2) In views, you can rearrange the fields you've selected, but also delete them. MerlinofChaos has got this figured out. I suggest looking at his code for inspiration. I won't have time myself for a couple of weeks.

  • 🇺🇸United States emergencyofstate

    +1 for a Drupal 7 solution for this. Is #19 in the running? Anyone else use it successfully?

  • 🇺🇸United States daggerhart

    Drupal 7 custom solution.

    I just posted in another thread my custom module that can create a remove button for selected 'unlimited' fields in Drupal 7.

    http://drupal.org/node/1143880#comment-5198566

  • 🇺🇸United States jschrab

    Will this work with image/file fields? Will the files get deleted or will there be dangling "fid's"?

  • 🇺🇸United States JordanMagnuson

    Thanks for the custom module daggerhart!

    Unfortunately it's not working for me :( . I have an unlimited node_reference field (references project), and it shows up on your module's admin page, but checking it doesn't change anything when I go to edit a node that uses that field... no remove item.

  • 🇺🇸United States daggerhart

    To my knowledge image and file fields have their own Remove button in D7. I specifically added a configuration page to this module where you select which fields to apply the new Remove button in order to prevent complications with fields that already have this functionality.

    I just tested Image and File fields on my D7 site, and they do already have a Remove button.

    But to answer your question, no this module does not remove or delete any files or images. I've looked into how the File field removes fields using legitimate drupal ajax, and plan to contribute a module that does this correctly, but have not had the time to implement it yet.

    Also, I found a tiny bug in the module and have fixed it. Everything should be working fine now.
    Here is a link to my blog post where you can download the module:
    http://websmiths.co/blog/drupal-7-unlimited-fields-remove-button

    Any suggestions are appreciated.

    edit: changed link to go to blog post, so I don't have to find all these links and change them after every update.

  • 🇺🇸United States daggerhart

    Hi davidlerin,

    I've updated the module to work with date fields, node & user references. The link above now goes to the updated module, but here it is again in case there is some sort of trouble.

    v0.3
    http://websmiths.co/sites/default/files/projects/unlimitedfield_remove-7...

  • 🇺🇸United States jschrab

    Is this an issue anymore with merlinofchaos's patch?
    http://drupal.org/node/1234574#comment-5232728

  • 🇺🇸United States daggerhart

    If I understand it correctly, his patch applies to the field collections module. This issue is about core functionality of a remove/delete button for any type of field. Am I mistaken in this?

  • 🇺🇸United States dww

    @daggerhart: #29 is correct.

    To that end, any chance you can put your D7 contrib module here on drupal.org (even if it's just a sandbox project for now) so that discussion and support of your custom solution can continue in a more appropriate channel and we can leave this issue for solving this problem in D8 core?

    Thanks!
    -Derek

  • 🇺🇸United States daggerhart

    @dww,

    Absolutely.

    I have created the sandbox project for this functionality as a D7 module here:
    http://drupal.org/sandbox/daggerhart/1363702

    Sorry to hijack the thread.
    Thanks,
    - Jonathan

  • 🇬🇧United Kingdom robcarr Perthshire, Scotland

    @daggerhart just downloaded your sandbox (version 7.x-0.3)

    Thanks so much for posting this online and it's a great solution to an infuriating problem in D7. Should be in Core IMHO.

    Thanks again - very grateful.

    Rob

  • 🇺🇸United States xjm

    Tagging.

  • 🇺🇸United States Albert Volkman

    Just checked out and used daggerhart's module. Worked perfectly. Thank you!

    daggerhart++

  • 🇺🇸United States bxCreative

    @daggerhart - works perfectly!! Thanks for the save :)

  • 🇮🇱Israel druvision

    The 'UnlimitedField Remove' module works great, but not for link fields. On link fields, the button doesn't appear at all.

    Use-case: If the link field title is mandatory, I can't remove it at all (which is why I needed the 'remove' feature on the 1st place). By making the title optional, I can delete it in the usuall, non-intuitive D7 method, by simply deleting the text inside the field. But then I can't make it mandatory.

    Ref: http://drupal.org/node/1616310

  • 🇬🇧United Kingdom colinmcclure

    @daggerhart - works very well even inside field_collection with my custom multi-value field. There is just one niggle I found when testing it out...

    If you have a field_collection set to cardinality -1 and you remove a field_collection container item with its Remove button then the remove button provided by module ceases to work until refresh.

    Despite this little niggle and very specific use case, it works a treat and saved me a heap of time! Thank-you!

  • 🇺🇸United States dremy

    Has anyone attempted this solution with Addressfield? Currently on node save, it adds the empty Addressfield and plots a point in the middle of the US in addition to the address submitted.

  • 🇧🇪Belgium swentel

    Marked #1120162: Add "delete" links to multivalue field values as duplicate. Assigning to myself, going to try this during the weekend.

  • 🇧🇪Belgium swentel

    Ok, this is too crazy to get it right before 1 december.

  • 🇬🇧United Kingdom Fidelix

    swentel, apparently there is more time to get this done:
    http://buytaert.net/drupal-8-feature-freeze-extended

  • 🇧🇪Belgium swentel

    Yeah, but I'm not sure whether I have time enough for an initial patch before 1 december to get it going enough :)

  • 🇵🇰Pakistan jibran Sydney, Australia

    Is it still a feature request or can we convert it to task? It is very basic functionality missing form core so IMO we can change it to task.

  • 🇵🇰Pakistan jibran Sydney, Australia

    Just confirmed with @swentel on IRC he also thinks it is a difficult task.

  • 🇧🇪Belgium swentel

    This will be D9 material I'm afraid unless someone gets on this.

  • 🇺🇾Uruguay rafamd

    Well .. given it's a normal priority task .. it wouldn't hurt in the 8.x queue for now?

    FWIW, +1 to this fix, good day you all! :)

    EDIT: oops, didn't mean to change version state, leaving it now, but please swentel or any other, adjust it as you see best.

  • I'd quite welcome this. In D7 I always had to create the remove button manually and this I think should be present in core.

  • BTW: additionaly I consider this quite important because if you have many values in a field and you manually remove the value, since there is no remove button, and you'll add another values you'll end up with empty fields and from UX POV it just looks bad.

  • Sheesh 2 years and 11 months old issue.

    Oh and I wanted to mention that this is a bit tricky only because fields are keyed by integers. If keys are changed to strings it's a piece of cake to programm the remove button functionality.

  • 🇺🇸United States geerlingguy

    Subscribing and adding that yes, this is a bit of a tricky problem to solve. There are few fields that implement a remove button (e.g. File/image fields and a few other custom fields), and it's hard (and annoying) to build in a 'Remove' button on custom multi-value fields, but it would be a huge UX win if we had this out of the box for multi-value fields.

    I'm coming over from #2035753: Add 'remove item' function to multi-value autocomplete widget .

    Note also that there's a contrib module that does what would be nice to do in core for multi-value fields: Multiple Fields Remove Button

  • However, Multiple Fields Remove Button does NOT support all modules / widgets, each needs to be separately programmed.

    Hopefully, Remove button is added to all multivalue fields in core for all modules / widgets

  • Hi,

    I found a solution, with reference to Make this work with all fields by default Closed: outdated , apply Patch #4 on multiple_fields_remove_button-7.x-1.4.zip shall work with all modules and widgets.

    Please take note that multiple_fields_remove_button-7.x-1.5.zip does NOT have the patch #4 committed, also not patchable with patch #4, already reported to the maintainer.

    Drupal 8 shall make this in core coz it's ridiculous without a Remove button for Multivalue fields since day one.

    Thanks

  • 🇫🇷France Artusamak Bzh

    Will not happen in 8.0.x-dev since it's not critical.

  • Hi,

    It's Absolutely Critical if your page has multivalue Field Collection, File, Image fields which already provided Remove button, the whole page will become Very Messy and NOT Standardized if some multivalue fields have Remove button while others do not.

    Remove button shall be made in core so as to provide a user-friendly, clean and standardized interface. Make this work with all fields by default Closed: outdated

    Thanks

  • 🇺🇸United States jojonaloha

    @paulwdru, I think @ARtusamak meant that it is not Critical based on the Priority levels of issues and because of that cannot go into 8.0.x since it does not qualify as an allowed change for patch releases . It looks like there is still a little time to get this in to 8.1.x , so a patch would be helpful.

  • 🇨🇳China jusfeel

    it's not working with editable display formatter so you won't clear the value through ajax save.

    As always, great software needs a "working" UI or it's just for small group of geeks.

  • @jusfeel,

    Drupal is always meant for Geeks, powerful & highly flexible, I love it.

    The only drawbacks of Drupal are confusing / unorganized / inconsistent User-Interface, not nicely touched up in the perspective of end users' experiences.

  • 🇩🇪Germany geek-merlin Freiburg, Germany

    Added to summary:

    Note: For 7.x there is a contrib module for that: https://www.drupal.org/project/multiple_fields_remove_button
    This issue is about adding that functionality to core.

  • 🇮🇱Israel Eyal Shalev

    I created a working remove item button in a custom field widget using the following code:

    
    
      /**
       * {@inheritdoc}
       */
      protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
        $elements = parent::formMultipleElements($items,$form,$form_state);
        $field_name = $this->fieldDefinition->getName();
        $parents = $form['#parents'];
    
        foreach (Element::children($elements) as $element_key) {
          if (is_int($element_key)) {
            $delta = $element_key;
    
            $wrapper_array = array_merge($parents, [
              $field_name,
              $delta,
              'remove',
              'item'
            ]);
            $wrapper_id = Html::getUniqueId(implode('-', $wrapper_array));
            $elements[$delta]['#prefix'] = "<div id={$wrapper_id}>";
            $elements[$delta]['#suffix'] = '</div>';
            $elements[$delta]['remove_item'] = [
              '#type' => 'submit',
              '#value' => $this->t('Remove item'),
              '#submit' => [[$this, 'removeItemSubmit']],
              '#ajax' => [
                'callback' => [$this, 'removeItemAjax'],
                'wrapper' => $wrapper_id,
                'effect' => 'fade',
              ],
              '#name' => implode('_', $wrapper_array),
              '#attributes' => ['class' => ['field-remove-item-submit']],
              '#limit_validation_errors' => [array_merge($parents, [$field_name])],
            ];
          }
        }
    
    
        return $elements;
      }
    
      public function removeItemSubmit(array $form, FormStateInterface $form_state) {
        $button = $form_state->getTriggeringElement();
    
        // Go one level up in the form, to the widgets container.
        $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
        $container_element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2));
        $field_name = $this->fieldDefinition->getName();
        $field_parents = $element['#field_parents'];
        $delta = $element['#delta'];
        $field_values = &$form_state->getValue($container_element['#parents']);
        unset($field_values[$delta]);
    
        // Increment the items count.
        $field_state = static::getWidgetState($field_parents, $field_name, $form_state);
        $field_state['items_count']--;
        static::setWidgetState($field_parents, $field_name, $form_state, $field_state);
    
        $form_state->setRebuild();
      }
    
      public function removeItemAjax(array $form, FormStateInterface $form_state) {
        return [
          '#type' => 'hidden',
          '#attributes' => [
            'data-description' => 'It is a requirement to return something to make an ajax call hide the entire element.'
          ]
        ];
      }
    
  • 🇩🇪Germany geek-merlin Freiburg, Germany

    Cool! so needs-review...

  • 🇮🇳India mayurjadhav Mumbai

    @Eyal Shalev file is present but your patch didn't applied, let me try this patch.
    If it gives the same result then i think bot Test has some issues, we need to work on them too.

  • 🇩🇪Germany geek-merlin Freiburg, Germany

    This would be easier to review if the (totally sensible) code style changes were not in it.

  • 🇮🇱Israel Eyal Shalev

    I tried to replace all the [] arrays to array(). This was done manually so it could fail.

  • 🇮🇳India mayurjadhav Mumbai

    Marking as Needs Review for #63 patch.

  • 🇩🇪Germany geek-merlin Freiburg, Germany

    Code looks good, but seems to need a rebase.

  • 🇮🇳India mayurjadhav Mumbai

    I could apply the patch, so no reroll/rebase is needed.

  • 🇩🇪Germany geek-merlin Freiburg, Germany

    See the testbot in #65:

    8.1.x: PHP 5.5 & MySQL 5.5 Patch failed to apply

  • 🇮🇳India mayurjadhav Mumbai

    @alex.rutz, Yes #65 patch failed to apply and bot test too. I was talking about #63.

  • 🇮🇱Israel Eyal Shalev

    I've found some bugs with the first patch.

    The changes are:

    • The ajax callback no longer return an empty element but the whole container.
    • The id_prefix and wrapper_id variables are moved up in the formMultipleElements method.
    • The submit method will now affect both the form_state values and the form_state user_input.
    • The submit method will now only unset the choosen item but move all the items after it one delta lower.
  • 🇮🇱Israel Eyal Shalev
    • Fixed the cause of the test fail.
    • Only add the remove button when the field cardinality is unlimited and it is not programmed.
  • 🇮🇱Israel Eyal Shalev

    Same as #74. I didn't patch against 8.1.x

  • 🇭🇺Hungary szato

    Re-test passed.

  • 🇩🇪Germany dropdiver

    This is not working in autocomplete term fields, i get an ajax-http Error!

  • 🇺🇸United States apraturu

    I am having an issue related to removing single items from a list on an edit form, both before and after installing patch #77. I'm wondering if anyone has had a similar issue. It happens when I do the following:

    1) Create a list of 3 items (A, B, and C) and save the list.

    2) Open the list for editing. As expected, the list appears on the edit form like this:
    1. A
    2. B
    3. C
    4.

    3) Remove item A and then item B. The list now appears like this, as it should:
    1. C
    2.

    4) Click the button to add another item to the list.

    The expected result is this list:
    1. C
    2.
    3.

    However, the actual result is this:
    1. C
    2.
    3. C

    The new item comes pre-populated with the item that was in that slot at the start, rather than an additional empty slot as expected. Any ideas on the cause/or solution to this would be extremely helpful. Thanks!

    Edit: You can reproduce this behavior on simplytest.me with a clean Drupal 8.1.8 install. Just add a new field to an article content type with unlimited values and follow the steps above and you'll see the same result.

  • 🇺🇸United States bleen

    I figured out the cause of the issue in #81 though I have no idea what the correct fix might be... Here's the problem:

    During the course of processing the form, the FormBuilder->rebuildForm() method is called. It, in turn, eventually calls a stack that looks like this:

    If you take a close look at EntityFormDisplay->buildForm() you see this:

    public function buildForm(FieldableEntityInterface $entity, array &$form, FormStateInterface $form_state) {
        // ... do some things
    
        foreach ($this->getComponents() as $name => $options) {
          if ($widget = $this->getRenderer($name)) {
            $items = $entity->get($name);  // <==== HERE"S YOUR PROBLEM
           // .... do more things
          }
        }
        // ... do more things
      }
    

    The problem is that $items gets set based on the cached $entity object without taking into account the changes that have already been made to the form as reflected in $form_state.

    A quick-and-hackey fix would be to let this happen and then after the fact, adjust $items based on $form_state , but this seems both wasteful and fragile.

    I think this is a core bug regardless and should maybe (probably?) be addressed outside of this issue, but I'm not sure. A LOT of dominos have to fall in just the right way, but this could lead to an editor inadvertently overwriting his/her own data if their site has code to manipulate the entity edit form the way that the functionality in this issue would if it ever gets in.

  • Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

    Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle .

  • 🇺🇸United States bleen

    Here testbot... Here boy

  • 🇭🇺Hungary dj1999

    Patch again.

  • 🇭🇺Hungary dj1999

    #77 patch working my fine except comment #80.

    Regarding #80 prepare a patch. Please review it.

  • 🇺🇸United States mpotter

    One problem with this patch is that the removeItemSubmit public function added by this patch is incompatible with the same function name used in the entity_browser module (entity_browser/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php)

    I suggest using a different name here so it doesn't break existing sites.

    Or, if entity_browser needs to update it's argument list, let me know and I'll submit that as an issue for them, but wasn't sure they would care about a conflict with a patch that hasn't been committed yet.

  • 🇺🇸United States bleen

    The patch in #85 has some issues...

    First, the "remove" button does nothing at all in the following circumstance:

    • Add a text field to a content type with unlimited cardinality
    • On the field setting form set the default values to "red" "white" and "blue" and save the field
    • Now edit the field
    • Notice that the "remove" button added to the default value fields doesn't do anything on that form

    Another issue (I'm 99% sure this is realted to #81/#82:

    • Add a text field with an unlimited cardinality to a content type
    • create a new node with "red" "white" and "blue" as values in that field (save the node)
    • Now edit the node.
    • Click the remove button next to "blue". You should now have "red" "white" and empty field but instead you have "red" blue" and empty field.
  • 🇨🇳China smiletrl

    I was working on this issue these days, and this is one widget working for my string text field. It uses some code from field_collection module, adds a few customization. Maybe someone can get some idea for your customization.

    
    namespace Drupal\bix_global\Plugin\Field\FieldWidget;
    
    use Drupal\Component\Utility\Html;
    use Drupal\Core\Field\FieldItemListInterface;
    use Drupal\Core\Form\FormStateInterface;
    use Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextfieldWidget;
    use Drupal\Component\Utility\NestedArray;
    use Drupal\Core\Field\FieldStorageDefinitionInterface;
    
    /**
     * Plugin implementation of the custom string widget.
     *
     * @FieldWidget(
     *   id = "bix_global_string_textfield",
     *   label = @Translation("BIX Global Textfield"),
     *   field_types = {
     *     "string"
     *   },
     * )
     */
    class BixGlobalStringTextfieldWidget extends StringTextfieldWidget {
    
      /**
       * {@inheritdoc}
       */
      public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
        $element = parent::formElement($items, $delta, $element, $form, $form_state);
    
    
        $field_name = $this->fieldDefinition->getName();
    
        // Nest the field collection item entity form in a dedicated parent space,
        // by appending [field_name, delta] to the current parent space.
        // That way the form values of the field collection item are separated.
        $parents = array_merge($element['#field_parents'], [$field_name, $delta]);
        //$parents = $form['#parents'];
        $element += [
          //'#element_validate' => [[static::class, 'validate']],
          '#parents' => $parents,
          '#field_name' => $field_name,
        ];
    
        // 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' => [
              '#delta' => $delta,
              '#name' => implode('_', $parents) . '_remove_button',
              '#type' => 'submit',
              '#value' => t('Remove'),
              '#validate' => [],
              '#submit' => [[static::class, 'removeSubmit']],
              '#limit_validation_errors' => [],
              '#attributes' => array(
                'class' => array('bix-global-custom-multi-field-remove-button'),
              ),
              '#ajax' => [
                'callback' => [$this, 'ajaxRemove'],
                'options' => $options,
                'effect' => 'fade',
                'wrapper' => $form['#wrapper_id'],
              ],
              '#weight' => 1000,
            ],
          ];
        }
        return $element;
      }
    
      /**
       * {@inheritdoc}
       */
      protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
        // We don't want to render empty items on field collection fields
        // unless a) the field collection is empty ; b) the form is rebuilding,
        // which means that the user clicked on "Add another item"; or
        // c) we are creating a new entity.
        if ((count($items) > 0) && !$form_state->isRebuilding() && !$items->getEntity()->isNew()) {
          $field_name = $this->fieldDefinition->getName();
          $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
          $parents = $form['#parents'];
          if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
            $field_state = static::getWidgetState($parents, $field_name, $form_state);
            $field_state['items_count']--;
            static::setWidgetState($parents, $field_name, $form_state, $field_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'] = '<div id="' . $form['#wrapper_id'] . '">';
        $elements['#suffix'] = '</div>';
        $elements['add_more']['#ajax']['wrapper'] = $form['#wrapper_id'];
        return $elements;
      }
    
      /**
       * Submit callback to remove an item from the field UI multiple wrapper.
       *
       * When a remove button is submitted, we need to find the item that it
       * referenced and delete it. Since field UI has the deltas as a straight
       * unbroken array key, we have to renumber everything down. Since we do this
       * we *also* need to move all the deltas around in the $form_state->values
       * and $form_state input so that user changed values follow. This is a bit
       * of a complicated process.
       */
      public static function removeSubmit($form, FormStateInterface $form_state) {
        $button = $form_state->getTriggeringElement();
        $delta = $button['#delta'];
    
        // 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, ['widget']));
    
        $field_name = $parent_element['#field_name'];
        $parents = $parent_element['#field_parents'];
    
        $field_state = static::getWidgetState($parents, $field_name, $form_state);
    
        // Go ahead and renumber everything from our delta to the last
        // 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, ['widget', $i + 1]);
          $old_element_state_address = array_merge($address_state, [$i + 1]);
          $new_element_state_address = array_merge($address_state, [$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.
          $moving_element['#parents'] = $new_element_state_address;
    
          // Move the element around.
          $form_state->setValueForElement($moving_element, $moving_element_value);
          $user_input = $form_state->getUserInput();
          NestedArray::setValue($user_input, $moving_element['#parents'], $moving_element_input);
          $form_state->setUserInput($user_input);
    
          // Move the entity in our saved state.
          if (isset($field_state['entity'][$i + 1])) {
            $field_state['entity'][$i] = $field_state['entity'][$i + 1];
          }
          else {
            unset($field_state['entity'][$i]);
          }
        }
    
        // Then remove the last item. But we must not go negative.
        if ($field_state['items_count'] > 0) {
          $field_state['items_count']--;
        }
        else {
          $form_state->setValue('empty_item', 1);
        }
    
        // Fix the weights. Field UI lets the weights be in a range of
        // (-1 * item_count) to (item_count). This means that when we remove one,
        // the range shrinks; weights outside of that range then get set to
        // the first item in the select by the browser, floating them to the top.
        // We use a brute force method because we lost weights on both ends
        // and if the user has moved things around, we have to cascade because
        // if I have items weight weights 3 and 4, and I change 4 to 3 but leave
        // the 3, the order of the two 3s now is undefined and may not match what
        // the user had selected.
        $input = NestedArray::getValue($form_state->getUserInput(), $address_state);
        // Sort by weight.
        // This sort function depends on field_collection module.
        uasort($input, '_field_collection_sort_items_helper');
    
        // Reweight everything in the correct order.
        $weight = -1 * $field_state['items_count'];
        foreach ($input as $key => $item) {
          if ($item) {
            $input[$key]['_weight'] = $weight++;
          }
        }
    
        $user_input = $form_state->getUserInput();
        NestedArray::setValue($user_input, $address_state, $input);
        $form_state->setUserInput($user_input);
    
        static::setWidgetState($parents, $field_name, $form_state, $field_state);
    
        $form_state->setRebuild();
      }
    
      /**
       * Ajax callback to remove a field collection from a multi-valued field.
       *
       * @param array $form
       * @param \Drupal\Core\Form\FormStateInterface $form_state
       *
       * @return \Drupal\Core\Ajax\AjaxResponse
       *   An AjaxResponse object.
       *
       * @see self::removeSubmit()
       */
      function ajaxRemove(array $form, FormStateInterface &$form_state) {
        // At this point, $this->removeSubmit() removed the element so we just need
        // to return the parent element.
        $button = $form_state->getTriggeringElement();
        $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -3));
        $delta = $element['#max_delta'];
    
        if ($delta === 0) {
          $empty_item_flag = $form_state->getValue('empty_item');
          // Reset the last item's value to be NULL.
          if ($empty_item_flag) {
            $element[$delta]['value']['#value'] = '';
          }
        }
        return $element;
      }
    
      /**
       * {@inheritdoc}
       */
      public static function addMoreSubmit(array $form, FormStateInterface $form_state) {
        $button = $form_state->getTriggeringElement();
    
        // Go one level up in the form, to the widgets container.
        $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
        $field_name = $element['#field_name'];
        $parents = $element['#field_parents'];
    
        // Increment the items count.
        $field_state = static::getWidgetState($parents, $field_name, $form_state);
        $field_state['items_count']++;
    
        static::setWidgetState($parents, $field_name, $form_state, $field_state);
    
        $form_state->setRebuild();
      }
    
      /**
       * {@inheritdoc}
       */
      public static function addMoreAjax(array $form, FormStateInterface $form_state) {
        $element = parent::addMoreAjax($form, $form_state);
    
        // Set the newest item empty value.
        $delta = $element['#max_delta'];
        $element[$delta]['value']['#default_value'] = '';
        $element[$delta]['value']['#value'] = '';
    
        return $element;
      }
    }
    
    
  • 🇺🇸United States mpotter

    smiletrl: rather than pasting a huge amount of text like that, it would probably be better to upload it as an actual file.

    bleen: I can confirm both of the issues you reported, so agree this needs more work.

  • Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. ( Drupal 8.3.0-alpha1 is available for testing.)

    Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle .

  • 🇭🇺Hungary szato

    Tested, works for me.
    One comment:
    Removing the last item removes the "Add another item" button too (because the whole wrapper is re-generated by ajax with no item).
    My solution:
    added checking:
    $field_state['items_count'] > 0
    to avoid deletion (displaying the remove button) when only one item is left.

  • 🇵🇱Poland tacituseu

    How about something simpler ?
    Adding column and a checkbox, that is used in WidgetBase::extractFormValues() to filter out unwanted values.
    The advantage is that it avoids AJAX callbacks, also you can change your mind and just un-check.
    Could also add JS/behavior for it that hides the checkboxes and adds buttons in their place (that just toggle the hidden checkboxes).

  • 🇮🇳India Munavijayalakshmi
    +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
    @@ -353,6 +369,11 @@ public function extractFormValues(FieldItemListInterface $items, array $form, Fo
    +        // Filter out removed items
    

    Comments should (noramlly) begin with a capital letter and end with a full stop / period .

    +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
    @@ -202,6 +202,22 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
    +          $element['_remove'] = array(
    +            '#title' => $this->t('Remove'),
    +            '#title_display' => 'invisible',
    +            '#type' => 'checkbox',
    +            '#default_value' => $items[$delta]->_remove ?: FALSE,
    +            '#weight' => 101,
    +          );
    

    use short array syntax (new coding standard).

  • 🇩🇪Germany rgpublic Düsseldorf 🇩🇪 🇪🇺

    Hm. Don't know if I'm mistaken but patch #97 seems to add the checkbox to every field - no matter whether it actually consists of multiple values (cardinality!=1) or not.

  • 🇵🇱Poland tacituseu

    @rgpublic: it is added inside if ($is_multiple) {...} right beside '_weight' so it really shouldn't

  • 🇩🇪Germany rgpublic Düsseldorf 🇩🇪 🇪🇺

    Ah, thanks @tacituseu. See it now. Did a mistake while adapting the patch to 8.2. It seems to work now. I've attached it in case anyone needs this.

  • 🇵🇱Poland tacituseu

    Adds class 'removed' to the row so it could be themed or just "display: none"d.

  • 🇵🇱Poland tacituseu

    Missed .once() in behavior, converted to button triggered to be consistent with field and image widgets.

  • 🇦🇺Australia GRO

    Re-roll of patch from #105 for 8.2.x

  • 🇺🇸United States bleen

    Can you please comment on whether the latest patch addresses #81/82 and #89

  • 🇵🇱Poland tacituseu

    @bleen: yes the alternative approach solves all of them by not messing with $form_state over AJAX.

  • 🇧🇪Belgium swentel

    The patch from 106 is hard to scan with the unrelated coding standards updates .. can we leave those out please. Also, we definitiely need for tests for this.

  • 🇵🇱Poland tacituseu

    Also needs work for fields with fixed (>1) cardinality, and no-js graceful degradation.

  • 🇦🇺Australia GRO

    Re-rolled patch from #106, removed coding standards updates and added a check of field items to only apply the remove button when the field is not a new/blank field item.

  • 🇺🇸United States bleen

    I had a chance to test the patch in #106 and I can confirm that it does address #81/#82 as well as #89.

    In addition I was not able to find any other problems when testing with textfields that have unlimited cardinality.

    thumbs up from me

  • 🇵🇱Poland tacituseu

    No-js graceful degradation and coding standard fixes from #112, left out "apply the remove button when the field is not a new/blank field item" as it forces you to "Add another item" to be able to delete last one, it makes sense in file/image widgets due to their nature but not on generic fields in my opinion.
    Still no tests,

  • 🇵🇱Poland tacituseu

    Minor CSS cleanup, swapping order of button and checkbox to get styles consistent with file/image widget.

  • Adding a fix to #112 to solve a double binding issue

  • 🇫🇷France andypost

    Test for last patch

  • 🇵🇱Poland tacituseu

    Could you please stop this nonsense of rolling for 8.3.x/8.2.x ?
    It is just pointless noise, there is no way this gets into 8.3.x.

  • 🇩🇪Germany rgpublic Düsseldorf 🇩🇪 🇪🇺

    @tacituseu: But you forget that this might be very helpful for people who currently cannot upgrade and need this.

  • 🇵🇱Poland tacituseu

    @rgpublic: I didn't, frankly they shouldn't use it on production, it's a hard problem, the issue was created in 2011 and is slow enough even without it, the proposed approach doesn't even have blessing of the system maintainers yet.

    What would be very helpful is tests ;) they would be useful even if approach changes.

  • 🇺🇸United States samuel.seide

    I tested the 8.4.x patch on a vanilla install of drupal 8.4.x dev, with the paragraphs and field_group module installed. If you create a field group add a entity reference paragraphs inside the group and give that a default paragraph, you will end up with this error:

    in the code, the error is originating from the code $remove_element = $item['_remove']; as the default paragraph doesn't have a value for $item['_remove'] and so it comes over as null.

    Here's how I had the paragraph setup in the content type:

  • 🇵🇱Poland tacituseu

    @samuel.seide: Thanks for testing, it happens because the code assumes (wrongly) that each field extending WidgetBase and overwriting ::formMultipleElements() will call parent::formMultipleElements(), but Paragraphs doesn't.
    I will post an updated patch in a couple of days.

    I'm not familiar with Paragraphs, is your expectation it will somehow 'work' with it, or just not break ?

  • 🇺🇸United States samuel.seide

    My expectation is that the patch should have some checking in it so that if the $item['_remove'] is not available then it won't run that code. Basically the patch should be able to function without it throwing up notices when a default paragraph is being used.

  • 🇮🇳India MRPRAVIN

    Adding the patch for removing the single item in the multiple text field values.
    It is working fine in 8.3.3 branch

    Please review

  • 🇵🇱Poland tacituseu

    @samuel.seide: Here it is, also updated for ES6.

  • 🇵🇱Poland tacituseu

    There's still some JS double-click issue when used within Paragraphs.
    Edit: Caused by nested '.field-multiple-table' tables.

  • 🇵🇱Poland tacituseu

    Should be fixed now.

  • 🇺🇸United States samuel.seide

    @MRPRAVIN Your patch contains a custom module in it - modules/custom/rb_account_profile/rb_account_profile.module b/modules/custom/rb_account_profile/rb_account_profile.module so there's no way to test it...

  • 🇺🇸United States samuel.seide

    Here is a patch that should work for 8.3.5. See 1038316-131-field-multiple-remove-item-alt.patch if you're running 8.4.x

  • 🇮🇳India MRPRAVIN

    @ samuel.seide Thanks for pointing one. I have updated the patch file now. Please test and let us know. May be this patch will be helpful :)

  • 🇮🇳India MRPRAVIN

    Here i have attached the patch after @samuel.seide comments reviewed. Please test this patch. I think it will help to someone in Drupal World :) It's works fine in 8.3 Drupal version. Need suggestions also please review.

  • 🇮🇳India MRPRAVIN

    @here Can someone point out me please why patch gets failed? Based on suggestions i'll work on it.

  • 🇺🇸United States bleen
    +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/StringTextfieldWidget.php
    @@ -23,29 +26,29 @@ class StringTextfieldWidget extends WidgetBase {
    -    return [
    +    return array(
             'size' => 60,
             'placeholder' => '',
    -      ] + parent::defaultSettings();
    +      ) + parent::defaultSettings();
    

    I'm not sure why the patch is failing to apply, however there was a coding standards change a few months ago (longer?) that says Drupal expects the "[" notation for arrays (see https://www.drupal.org/docs/develop/standards/coding-standards#array ) so this change (which is where the patch fails) is not correct anyway and should be removed from the patch... the same is true of other array notation regressions throughout the patch.

    I'd bet a dollar that if you reroll the patch without those coding style regressions that all would be well with the world

  • Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle .

  • 🇮🇳India MRPRAVIN

    @bleen, Thank you for point out me :) Notation for arrays are changed in coding standards. you are correct.

  • 🇦🇺Australia realityloop

    rerolled against 8.4.x for my own use

  • heddn Nicaragua

    There seems to be two competing approaches contending in this issue. #143 is with ajax and #145 is with checkboxes. Can we get an IS update and proposed resolution? The issue description is fine, but I don't see what/how we want to fix this agreed upon yet. That makes it really hard to review and provide feedback on patches, because I don't know which is the preferred approach. I only read back through the last 75 or so comments for the past year, and there wasn't any real sense on what has been decided. Can we decide on that before throwing more patches against this thing?

  • 🇵🇱Poland tacituseu

    @heddn: #85 📌 Allow for deletion of a single value of a multiple value field Fixed is the last
    comprehensive patch on the AJAX path, but as mentioned in #89 📌 Allow for deletion of a single value of a multiple value field Fixed it has some problems.

    There's a lot of noise - people learning how to make patches, incomplete solutions, re-rolls for obsolete versions, #143 📌 Allow for deletion of a single value of a multiple value field Fixed is just a last of "works for my limited use-case" patches (only textfield - started in #128 📌 Allow for deletion of a single value of a multiple value field Fixed ).

  • heddn Nicaragua

    So, one issue with #143 is that it only supports text fields. And the approach won't scale very well to non-text fields. Meaning, what is built there won't shoehorn into any other field types. So... back to the drawing board?

  • heddn Nicaragua

    I spent a fair amount of time banging away at this today. I figured out one thing here close the end of the day... we won't be able to do this without a some changes to field widget base. The latest non JS patch in #143 was done entirely on the sub widget. That doesn't work if you have ajax refreshing in the field. Which I have on my custom field. I'll have to changes some things up in the widget base. Because that's where the item_count is getting calculated and used in formMultipleElements(). In the $max variable.

    No patch, because I don't have any code. Just lots of time spent in xdebug trying to figure out this special edge case.

  • heddn Nicaragua

    Here's a patch (it doesn't work yet). It doesn't seem to be removing the items. But, its moderate progress as we now are moved up to the base class. So all field types will eventually get this functionality.

  • heddn Nicaragua

    Here's a new patch that seems to work in all cases for my address field. It hasn't been extensively tested, but at least it seems to be functional. Sorry about the interdiff; I don't think it is very readable.

  • heddn Nicaragua

    OK, this is a proper interdiff for 152.

  • heddn Nicaragua

    Here's what it looks like fully loaded with several values/locations:

    I've added a generous number of example locations on the page. I can remove any one of them or add another one.

    When the page first loads with no values/locations, it looks like the following:

    One must click add to add the first value/location.

  • Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle .

  • heddn Nicaragua

    Based on some feedback from @alexpott in slack, I've improved the button language to read, "Add item" or "Add another item" depending on if this is the initial item or not.

    Also, flagging for usability review.

  • 🇬🇧United Kingdom alexpott 🇪🇺🌍

    Reviewing #152.

    1. +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
      @@ -208,36 +208,64 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
      +      $wrapper_id = Html::getUniqueId($id_prefix . '-add-more-wrapper');
      

      The wrapper is being used for more than adding more. It is also being used for removing.

    2. +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
      @@ -208,36 +208,64 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
      +      foreach ($elements as $element) {
      +        $elements['#prefix'] = '<div id="' . $form['#wrapper_id'] . '">';
               $elements['#suffix'] = '</div>';
      +        $elements['add_more']['#ajax']['wrapper'] = $form['#wrapper_id'];
      +      }
      

      This looks completely unnecessary.

    3. +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
      @@ -208,36 +208,64 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
      +        '#value' => t('Add another item'),
      

      Could do something like '#value' => $delta > 0 ? t('Add another item') : t('Add item'),
      Not adding an item by default makes required fields in the single widget form work really nicely.

    4. +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
      @@ -208,36 +208,64 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
      +        '#limit_validation_errors' => [array_merge($parents, [$field_name])],
      

      I'd have this set to [] because clicking add more shouldn't validate the exist elements imo.

  • 🇬🇧United Kingdom alexpott 🇪🇺🌍

    Also thinking about this some more I thing the remove thing should be optional - or maybe a whole new WidgetBase so existing code and alters still work as expected.

  • 🇬🇧United Kingdom alexpott 🇪🇺🌍
    +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
    @@ -208,36 +208,63 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
    +      $form['#wrapper_id'] = $wrapper_id;
    ...
    +              'wrapper' => $form['#wrapper_id'],
    

    I don't think we need $form['#wrapper_id'].

  • 🇨🇭Switzerland Berdir Switzerland
    +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
    @@ -208,36 +208,63 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
    +      $id_prefix = implode('-', array_merge($parents, [$field_name]));
    +      $wrapper_id = Html::getUniqueId($id_prefix . '-add-wrapper');
    

    I'm wondering in general why it uses a *unique* ID. ajax IDs actually shouldn't be unique because the ajax systems relies on theming being not. For example, it automatically prepends messages to the replaced element. That only works if the ID does not change after the initial replacement happened as otherwise the second ajax command won't be able to prepend before the old ID.

    I changed the paragraphs widget recently to a non-unique ID in #2939718: Remove validation of closed paragraphs, fix validation and display of validation message for collapse and it worked fine. Probably the only exception to that would be if you'd have multiple entity forms on the same page but I don't think that is supported, at least not for the same entity type.

    I guess it doesn't matter that much for core as both add and remove ignore validation errors and are not expected to have any messages.

  • 🇬🇧United Kingdom alexpott 🇪🇺🌍
    +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
    @@ -208,36 +208,63 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
    +      for ($delta = 0; $delta <= $max; $delta++) {
    +        $elements[$delta]['actions'] = [
    +          '#type' => 'actions',
    +          'remove_button' => [
    +            '#delta' => $delta,
    +            '#name' => implode('_', array_merge($elements[$delta]['#field_parents'], [$field_name, $delta])) . '_remove_button',
    +            '#type' => 'submit',
    +            '#value' => t('Remove'),
    +            '#validate' => [],
    +            '#submit' => [[$this, 'ajaxSubmitRemove']],
    +            '#limit_validation_errors' => [],
    +            '#attributes' => [
    +              'class' => ['remove-field-delta--' . $delta],
    +            ],
    +            '#ajax' => [
    +              'callback' => [$this, 'removeAjaxContentRefresh'],
    +              'effect' => 'fade',
    +              'wrapper' => $form['#wrapper_id'],
    +            ],
               ],
    

    If we move the wrapper ID creation to before the loop where we create each single item form we could put this in there and do less looping.

  • 🇵🇱Poland tacituseu

    And from that I guess... consensus has been reached, before another patch was thrown ? ;)

  • heddn Nicaragua

    re #165: not necessarily, but the use of form_state and #ajax does seem to be functional. And its a design paradigm that seems to resonate with a lot of Drupal users.

    Ignore the patch in #158. It was overly ambitious. Here's a re-worked on top of #152 and addresses most of the feedback since then. The only thing I haven't touched is how to make it toggleable as suggested in #161.

  • 🇳🇱Netherlands idebr

    Re #163

    I'm wondering in general why it uses a *unique* ID. ajax IDs actually shouldn't be unique because the ajax systems relies on theming being not. For example, it automatically prepends messages to the replaced element. That only works if the ID does not change after the initial replacement happened as otherwise the second ajax command won't be able to prepend before the old ID.

    The id attribute must be unique in a document according to the html specifications:

    When specified on HTML elements, the id attribute value must be unique amongst all the IDs in the element’s tree and must contain at least one character. The value must not contain any space characters.

    https://www.w3.org/TR/html5/dom.html#element-attrdef-global-id

  • 🇨🇭Switzerland Berdir Switzerland

    That's not the point. I'm refering to the unique ID API which adds a random suffix.

    Of course it has be unique client-side, but when the ajax update happens then we replace that ID with the new content, which then can have the same ID again, there is nothing wrong with that.

    If you look at other #ajax, then they almost always even use custom #prefix/suffix with hardcoded markup, exactly to prevent that Drupal messes with those ID's in any way.

  • Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle .

  • 🇩🇪Germany jan.stoeckler

    Reroll & small tweaks; AJAX-callback should work now. Can't figure out how to generate an interdiff as patch file from #166 doesn't seem to apply.

  • 🇩🇪Germany jan.stoeckler

    Addresses/(hopefully) fixes some of the test failures.

  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul
    1. +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
      @@ -215,45 +217,64 @@ abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface
      +              '#submit' => [[get_class($this), 'submitRemove']],
      ...
      +                'callback' => [get_class($this), 'removeAjaxContentRefresh'],
      ...
      +        '#submit' => [[get_class($this), 'addMoreSubmit']],
      ...
      +          'callback' => [get_class($this), 'addMoreAjax'],
      

      Is get_class($this) necessary? Or will $this suffice?

    2. +++ b/core/lib/Drupal/Core/Field/WidgetBase.php
      @@ -320,6 +341,67 @@ abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface
      +    return NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -4));
      

      This seems like it might be cause of the test failures. Why -4?

  • 🇩🇪Germany jan.stoeckler

    1. Is get_class($this) necessary? Or will $this suffice?

    I'm not sure, both seem to work, as well as static::class - don't have a preference & will implement whatever's most appropriate.

    2. This seems like it might be cause of the test failures. Why -4?

    As far as I can tell, we use -4 here to get "up" in the nested array from the triggered submit element to its field element to replace its previous state with it (the whole field element).

  • 🇩🇪Germany jan.stoeckler

    Something that does not yet seem to work correctly: removing items after having changed their order (without saving in between) - this probably needs to be addressed.

  • 🇩🇪Germany tstoeckler Essen, Germany
    1. Is get_class($this) necessary? Or will $this suffice?

    I'm not sure, both seem to work, as well as static::class - don't have a preference & will implement whatever's most appropriate.

    Right, I would go with static::class, as well. As far as I know that always returns the same as get_class($this) (although PHP being PHP after all, I wouldn't be surprised to learn that there are exceptions that that... ;-)).

    Regarding the suggestion to just use $this, i.e. to specify [$this, 'submitRemove'] as the callback: While that is a valid PHP callable and - in general - is actually the preferred way because it doesn't require the method in question to be static, it should not be used in the Form API / Render array context, because it is not serializable and Form/Render array may be serialized. That's why I think [static::class, 'submitRemove'] is the way to go.

  • heddn Nicaragua

    Changed get_class($this) vs static::class

  • 🇸🇪Sweden Peter Törnstrand

    I have tested this patch with a content type, Address, with 3 different address fields:

    Visiting address
    Postal address
    Delivery address

    All fields can have multiple (unlimited) values.

    I edit an existing Address with 1 value for each field.
    I first delete the delivery address value.
    I then delete the postal address value.

    The postal address is deleted but is replaced with the data from the previously deleted delivery address.
    The field label of field Postal address is also changed to Delivery address.

    Drupal 8.5.6
    Address 8.x-1.4
    Patch from #179

    See https://www.drupal.org/project/address/issues/2992810 for screenshots.

  • Hi Peter. I think I'm experiencing similar issues to yours. Is your address field inserted within some sort of wrapper for the form display? I.e. a paragraph reference, a container/fieldset/details/form group, etc, or anything like that? A site I tested this on is using the Link field within a nested paragraph and all the fields we wanted the 'Remove' button on was nested using a form group. I'm not sure what kinds of nesting might affect this but the Form Display for this specific paragraph type wraps all of our components within a 'field_content'. Thus the form displays within a container class on the front-end.

    I think this wrapper was breaking the patch at Line 371. As such, I've made a slight correction to it which got it to work on my machine. It was looking for a value $user_input[$field_name] but it seems that it's not finding the value because it's not prefixing the form array with the correct keys. YMMV as I'm not entirely too sure what the actual underlying issue is.

    Patch attached below.

  • Sorry, some debugging statements got left behind. Here is a cleaner patch:

  • 🇫🇷France progzy

    I've tested with patch #183 and got issue.
    I've used Paragraphs module (latest stable version just like Drupal , 8.6.5, installed with composer)

    I've created a custom content type.
    I've created 1 paragraph with a single plain text field and a unlimited link field.
    I've added this paragraph as a unlimited field to this content type.

    Then created content from previous content type and add 2 instances of the paragraphs with 3 links for each. Saved.

    Now on edition if I try to remove second link of first paragraph it is actually removed. Then I try to remove second link of second paragraph, nothing happens (except spinner) when I click "Remove" (Even if I can see an AJAX call in debugger). I click "remove" again and everything is messed up.

    Some other combinations also lead to a messy result as well.

  • @progzy Thanks for the feedback.

    I tried to recreate the same situation as yours. I added the text fields (limit 1) and links (unlimited) to the paragraph and then added the paragraph to a content type with unlimited cardinality. But when I hit 'Remove' on the 2nd paragraph instance, 2nd link, it successfully deleted it.

    I'm going to try and recreate your bug on a fresh install but I thought I'd get back to you asap first. If you could, and if you think it's relevant, could you paste the 'Response' from the AJAX call that you see in the debugger?

    Running D8.6.5, will try on 8.6.7 but I don't think there should be much of a difference. (Edit: oh I thought I saw you were on 8.6.7 but you're also on 8.6.5. Perhaps one of my custom modules is interfering so regardless, I'll try this patch on a fresh install and see what happens).

  • 🇫🇷France progzy

    @cchoe1 you're welcome.

    So I did it again. I did it at https://d0cq3.ply.st (admin/admin as user/pwd) so you can try by yourself (It only lasts 12h though). Any way here are the AJAX responses :

    - First remove attempt that goes well: https://pastebin.com/raw/WyUbVTZX
    - Second remove attempt that fails: https://pastebin.com/raw/THFdp2xS

    Hope this helps.

    Conf:
    - Drupal 8.6.5 (not sure it your patch works with 8.6.7)
    - Paragraphs 1.5
    - Patch https://www.drupal.org/files/issues/2018-12-17/1038316-183.patch

  • Awesome, thanks for setting that up @progzy, I did confirm it was still messing up with your test environment. Looks like when I try to delete l1.2, it replaces that reference with the second paragraph. But then if I try to delete something from l2.*, it brings back l1.*. I'll look into my patch and see if I can't figure out the problem.

  • I've attached a patch below, I would not consider my changes to be good code but it makes a proof of concept and seems to work (solves the case above). I've attached a 'TODO: refactor' where we would want to come up with a more intuitive way of assigning a unique ID to the #name attribute.

    Drupal Form API requires a '#name' attr on the form element to find the correct triggeringElement. Originally, there would be naming collisions when you have both:
    - elements that have the same parent
    - elements that are using the same field

    When the form is being built, it attaches a delta to the name in an attempt to resolve this. This does not work in all cases--in this case, we are using 2 instances of the same paragraph type. It duplicates the names because deltas will be unique within the paragraph instance but not on the form itself, so two instances of identical paragraph types will result in fields with the same #name such as 'field_link_1_remove_button'.

    Now, there should be no duplicated names on the form. Look for the '@TODO' tag (line 225). I'm not sure if this is the best way to name the Remove buttons or not.

    To create the unique name, I simply implode the array_parents--no matter what element/field/grouping, this series of items in array_parents should be unique for any form element because it also keeps track of deltas across the entire render array--and then we also attach the local delta (it's position amongst its sibling elements) since that won't be present on array_parents.

    Some reading I did:

    https://drupal.stackexchange.com/questions/154387/why-does-triggering-el...

    https://www.drupal.org/project/drupal/issues/2546700 Add support for detecting the triggering element when buttons are changed client-side Needs work

  • Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle .

  • 🇨🇦Canada phjou Vancouver/Paris 🇨🇦 🇪🇺

    I just tested the patch and when I am using the remove button, it removes all the entities of the field instead of removing a specific element.
    I'm using this patch with Inline Entity Form as described in this issue: UX issues with the Simple widget

  • 🇳🇱Netherlands rfbrandsma

    I can confirm the issue phjou addresses.

    I would also like to report another issue with the patches so far:
    The wrapper id for the add more button no longer is unique. In Drupal core the Html::getUniqueId($id_prefix . '-add-more-wrapper'); is used to create a wrapper_id. The patches are using $wrapper_id = Html::cleanCssIdentifier($id_prefix . '-add-wrapper');.

  • 🇳🇱Netherlands rfbrandsma

    I can confirm the issue phjou addresses.

    I would also like to report another issue with the patches so far:
    The wrapper id for the add more button no longer is unique. In Drupal core the Html::getUniqueId($id_prefix . '-add-more-wrapper'); is used to create a wrapper_id. The patches are using $wrapper_id = Html::cleanCssIdentifier($id_prefix . '-add-wrapper');.

  • 🇫🇷France prudloff Lille

    I tried the patch in #188. When adding a value to a multivalued field, the AJAX request fails with this error :
    Notice : Undefined index: #field_parents dans Drupal\Core\Field\WidgetBase->formMultipleElements() (ligne 227 de /var/www/html/web/core/lib/Drupal/Core/Field/WidgetBase.php)

  • 🇳🇱Netherlands SpadXIII

    I noticed a little typo in the patch, so here's an updated version.

  • 🇳🇱Netherlands SpadXIII

    I added the getUniqueId-suggestion by rfbrandsma to the patch in #188. Other than this addition, the patch is identical to the #188 one.

    I just saw the notice mentioned by prudloff. This was with a field that had no parents.

  • 🇳🇱Netherlands Tess Bakker

    The Action type element, introduced in #128 conflicts with the Javascript implementation in Drupal.behaviors.dialog.prepareDialogButtons.

    Buttons of multiple field values are now displayed in the button area of a dialog.

    Screenshot:

  • I've attached a new patch which seems to fix the previously noted issue. Hope this helps

  • Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles .

  • 🇧🇾Belarus sanfair

    Fix some tests and notices

  • 🇧🇾Belarus sanfair

    Fixed for nested fields

  • 🇺🇦Ukraine semjuel

    I wonder, why don't use Multiple fields remove button module - https://www.drupal.org/project/multiple_fields_remove_button ?
    The module version for Drupal 8 works perfectly for me.

  • 🇺🇸United States dww

    @semjuel: Because this is a task for Drupal core itself. We want the core system to be able to support this functionality, without relying on a contrib module.

    Cheers,
    -Derek

  • 🇬🇧United Kingdom alexpott 🇪🇺🌍

    I'm upgrading this to a major task based on the usability and feel of adding an text field with unlimited cardinality to core. The result is very confusing because it is not at all apparent how to remove a value once you've added one. I'd go so far to say that the current behaviour is very surprising and not at all accessible.

  • Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles .

  • 🇬🇧United Kingdom MrPaulDriver

    @semjue and @dww, yes this does need to be in core.

    From screenshots in this issue it appears that efforts are being directed at a text based remove button.

    The text button approach was originally used by Multiple Fields Remove Button module, but this has more recently been replaced with a simple delete icon. This works a lot better.

    The same approach is also consistent with core ui as used in the media library module.

    Please see screenshots

  • 🇨🇿Czech Republic Denisev

    Tried the patch from #204 for list items inside paragraphs. Having list item 1, 2, 3 and changing their order with dragging to 3, 2, 1 and then deleting the middle number 2, the order reverses after save back to 1, 3 instead of being the expected 3, 1.

  • 🇨🇿Czech Republic Denisev

    This is a modified version of the patch in #204. The changes address the issue with loosing the order of items after save, when having removed items and also reordered them before. There are no new lines added, only a couple removed. Not sure, if this is the correct approach and if nothing else will break. So far it works in our environment.

  • 🇺🇸United States electrokate

    Neither #204 or #212 is working for me in Drupal 8.8.6. (#212 would not apply, so I made the changes manually to try it). Upon saving, it takes the last element in the $input array and assigns it a negative weight, such that it appears first in the list.

    I am trying to fix this but could use some assistance! I can't seem to edit elements of the $input array in the submitRemove function. Perhaps the change needs to be made in the removeAjaxContentRefresh function?

    EDITED TO ADD:

    I have edited the code and it works for me now. I replaced the submit function as follows:
    (Basically the "--key" needs to be inside the if statement that checks for the removed element, and the indices need to be reset afterward.)

        if ($exists) {
          $field_values = [];
          foreach ($field_input as $key => $input) {
            if (is_numeric($key) && $key >= $delta) {
              if ((int) $key === $delta) {
                --$key;
                continue;
              }
            }
            $field_values[$key] = $input;
          }
          NestedArray::setValue($user_input, $parent_element['#parents'], $field_values);
          $form_state->setUserInput($user_input);
        }
    
        unset($parent_element[$delta]);
        NestedArray::setValue($form, $array_parents, $parent_element);
    
        if ($field_state['items_count'] > 0) {
          $field_state['items_count']--;
        }
    
        $user_input = $form_state->getUserInput();
        $input = NestedArray::getValue($user_input, $parent_element['#parents'], $exists);
        $weight = -1 * $field_state['items_count'];
        foreach ($input as $key => $item) {
          if ($item) {
            $input[$key]['_weight'] = $weight++;
          }
        }
        // Reset indices.
        $input = array_values($input);
    
        $user_input = $form_state->getUserInput();
        NestedArray::setValue($user_input, $parent_element['#parents'], $input);
        $form_state->setUserInput($user_input);
        $user_input = $form_state->getUserInput();
        static::setWidgetState($parents, $field_name, $form_state, $field_state);
        $form_state->setRebuild();
    }

    Will follow up with patch.

  • 🇷🇺Russia maximpodorov

    Wow. Both issues are closed now. Is this what you planned?
    Oh. This one is not closed.

  • 🇺🇸United States bobbygryzynger United States

    It looks like the behavior described in 🐛 Item removal AJAX causes infinite nesting Fixed is also occurring here since much of the behavior from the contrib module was ported over.

  • Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle .

  • 🇳🇿New Zealand quietone New Zealand

    Closed 🐛 Deleting the first value in a required, multivalue field fails validation Closed: duplicate as a duplicate and adding credit.

  • 🇮🇳India anmolgoyal74

    Re-rolled #214 for 9.2.x. And also updated one of the failing tests.
    Not able to generate the interdiff.
    Here is the diff for the test.

    diff --git a/core/tests/Drupal/FunctionalTests/Entity/ContentEntityFormFieldValidationFilteringTest.php b/core/tests/Drupal/FunctionalTests/Entity/ContentEntityFormFieldValidationFilteringTest.php
    index 7bbcd1b27f..f5e7faa450 100644
    --- a/core/tests/Drupal/FunctionalTests/Entity/ContentEntityFormFieldValidationFilteringTest.php
    +++ b/core/tests/Drupal/FunctionalTests/Entity/ContentEntityFormFieldValidationFilteringTest.php
    @@ -135,9 +135,8 @@ public function testFieldWidgetsWithLimitedValidationErrors() {
         // The 'Test multiple' field is the only multi-valued field in the form, so
         // try to add a new item for it. This tests the '#limit_validation_errors'
         // property set by \Drupal\Core\Field\WidgetBase::formMultipleElements().
    +    $this->submitForm([], 'Add item');
         $assert_session->elementsCount('css', 'div#edit-test-multiple-wrapper div.form-type-textfield input', 1);
    -    $this->submitForm([], 'Add another item');
    -    $assert_session->elementsCount('css', 'div#edit-test-multiple-wrapper div.form-type-textfield input', 2);
     
         // Now try to upload a file. This tests the '#limit_validation_errors'
         // property set by
    @@ -150,20 +149,19 @@ public function testFieldWidgetsWithLimitedValidationErrors() {
         $this->submitForm($edit, 'Upload');
         $assert_session->elementExists('css', 'input#edit-test-file-0-remove-button');
     
    -    // Make the 'Test multiple' field required and check that adding another
    -    // item throws a validation error.
    +    // Make the 'Test multiple' field required and check saving the form
    +    // without entering any value for the required field still throws the
    +    // proper validation errors.
         $field_config = FieldConfig::loadByName($this->entityTypeId, $this->entityTypeId, $this->fieldNameMultiple);
         $field_config->setRequired(TRUE);
         $field_config->save();
     
    -    $this->drupalPostForm($this->entityTypeId . '/add', [], 'Add another item');
    -    $assert_session->pageTextContains('Test multiple (value 1) field is required.');
    +    $this->drupalGet($this->entityTypeId . '/add');
     
         // Check that saving the form without entering any value for the required
         // field still throws the proper validation errors.
         $this->submitForm([], 'Save');
    -    $assert_session->pageTextContains('Test single field is required.');
    -    $assert_session->pageTextContains('Test multiple (value 1) field is required.');
    +    $assert_session->pageTextContains('This value should not be null.');
       }
     
     }
    
  • #214 is working for me on 8.9.x but only for unlimited fields. I added a condition to hide the button when the amount of values is limited.

  • 🇮🇳India mohit_aghera Rajkot

    Changing to needs review so test bot can trigger pipelines.

  • 🇮🇳India ankithashetty Karnataka, India

    Just fixed the custom command failure errors in #225, thanks!

  • 🇳🇿New Zealand scott.whittaker

    The patch in #228 works for me, except that in my case I have a field with unlimited cardinality and when I edit an existing piece of content which has n items, it only shows n-1 items.

    In WidgetBase.php I needed to change the $max from $field_state['items_count'] - 1 to $field_state['items_count'] in order to fix this bug:

        // Determine the number of widgets to display.
        switch ($cardinality) {
          case FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED:
            //$max = $field_state['items_count'] - 1;
            $max = $field_state['items_count'];
            $is_multiple = TRUE;
            break;
    
  • 🇳🇿New Zealand scott.whittaker

    Patch based on #228 to fix the unlimited cardinality bug.

  • 🇮🇳India Lal_

    Triggering the test bots

  • 🇮🇳India Lal_

    Rebasing #232 to the 9.1.5 version

  • 🇮🇳India anmolgoyal74

    Fixed CS issue.

  • Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle .

  • 🇺🇦Ukraine trishen Kharkiv

    Drupal 9.1.5
    patch #235

    Bug: pre-filled fields after deletion
    Steps to reproduce:
    1. Add an unlimited link field to node type
    2. Create a node with multiple links
    3. Save node
    4. Edit node
    5. Remove one item
    6. Add another item
    7. New item is already filled

    Same behavior with module multiple_fields_remove_button

  • 🇺🇦Ukraine alphawebgroup Kiev

    Why do we need that in core? Why not in cotrib module like we had in D7 ?

  • 🇧🇪Belgium rp7

    @alphawebgroup I believe #209 sums that up pretty well.

  • 🇻🇳Vietnam linhnm

    #235 is working for me on 9.2.6 but only for unlimited fields. I added a condition to hide the button when the amount of values is limited.

  • 🇺🇸United States neclimdul Houston, TX

    Love finding an issue so old there are people subscribing with comments...

    I've cleaned some things, reverted some changes that seemed unrelated, and applied some earlier fixes that had been lost. Some of those lost fixes like clearing out the form array so config doesn't try to save the remove button should clear up some test failures.

    I haven't really finished reviewing the submit handlers. The parent slicing makes me a little nervous because it seems kinda fiddly/brittle since its tied to the structure of the form. Skimming the thread that seems to be confusing other people as well. Submitting now because I wanted to see how this affected the rest of the test suite.

    Also, clearly needs tests but that's tagged. Noting because its not really ready for review, just running testsuite.

  • Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle .

  • 🇨🇦Canada maursilveira Windsor, ON

    Hello,

    I tested the patch #242 and it works as expected.

    Thank you!

  • 🇫🇷France DrDam

    Hi,

    Test against 9.4.x and 9.5.x=> this work fine

    Thanks

  • 🇸🇪Sweden ordermind

    For me, if I click on the remove button when there is only one field value with data that has been saved to the database already, such as when editing an existing entity, the saved values are displayed again rather than empty values which is the expected behavior according to me.

  • 🇸🇪Sweden ordermind

    Another bug I found was that if you delete a value as well as the empty "new value" item, and then click on the button to add another value, the value that you deleted will be displayed. According to me, the expected behavior is that it should be empty.

  • 🇸🇪Sweden ordermind

    *** comment removed, this issue was due to code outside of this module ***

  • First commit to issue fork.
  • @siva01 opened merge request.
  • 🇮🇳India Abhijith S

    Applied patch #242 on 9.4.x.The Remove button appears which can be used to remove field items after applying this patch.
    Attaching screen recordings for reference.

  • @siva01 opened merge request.
  • First commit to issue fork.
  • Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle .

  • The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

    Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

  • 🇺🇸United States brad.bulger

    Coming here from #93447 with a slight variation - this seems like a huge change to handle the problem.

    Will this support a programmatic way to remove values from a multiple value field?

    What I am seeing, in 9.5, is that if you have a required multiple value field, and you move the first item out of the first position on the form, it still retains its #required status, and still will fail with a required field error if blank, even though the "first" (from the user's perspective) element in the field has a value.

    In my case, the user is not removing the value, my code is making the change. I need to be able to set an item's value to blank, whether it used to be the first item or not. The only workaround at the moment is to rebuild and resort all of the values, and even that is troublesome.

    It seems like a bug in its own right that this fails, quite apart from adding a "remove" button as a new feature or whatever.

  • 🇨🇭Switzerland Berdir Switzerland

    This is solely a UI issue. If you want to remove an item from a field, you can use \Drupal\Core\TypedData\ListInterface::removeItem().

  • 🇺🇸United States brad.bulger

    Not from any hook functions that can modify a node form, for instance. The items list is not available, only arrays - user input, form state values, the form array.

  • Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened , as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle .

  • This issue is specifically about making the user interface useable. If you have a problem doing something programmatically, you should create a separate issue.

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    28,520 pass
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    29,414 pass
  • 🇳🇱Netherlands perry.franken

    When a site is multilingual the "Add another item" string is not translated. To fix this i created a patch that always uses the singular value.

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    30,306 pass, 19 fail
  • 🇳🇱Netherlands perry.franken

    Intern diff of patch 242 & 262

  • 🇳🇱Netherlands perry.franken

    use "Add another item" instead of "Add item"

  • 🇳🇱Netherlands perry.franken

    This time with patch.

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    30,341 pass
  • 🇫🇮Finland lauriii Finland

    We added the remove button inline with the input in 📌 List key|label entry field is textarea, which doesn't give guidance towards the expected input Fixed . Could we do the same here?

  • First commit to issue fork.
  • last update 12 months ago
    29,800 pass, 2 fail
  • Status changed to Needs review 12 months ago
  • 🇷🇴Romania vasike Ramnicu Valcea

    created a new MR for 11.x ... for anyone to join and "close" this ancient issue
    - Started from the latest previous patch - #265
    - Small refactor there
    - Change FormJSAddMoreTest.php to MultipleValueWidgetTest.php to test more things on multiple value widgets ... than just Add more button.
    And test remove buttons there.

    On #266

    We added the remove button inline with the input in #2521800: List key|label entry field is textarea, which doesn't give guidance towards the expected input. Could we do the same here?

    I think it's already quite the same.

  • last update 12 months ago
    29,801 pass
  • 🇺🇸United States smustgrave

    See tests have been added so think we can remove that tag

    Leaving issue summary if there are anything that needs to be added

    Testing by adding a text field
    Set to unlimited
    Added 3 items to the field
    Verified they all get the Remove button
    Removed the middle item
    Everything saved correctly

    But visually could use some tweaks.

    Maybe could be done in a follow up but think it looks like a regression too much.

  • Status changed to Needs work 12 months ago
  • 🇺🇸United States smustgrave

    oops actually get @lauriii requested the same in #266

  • Status changed to Needs review 12 months ago
  • 🇷🇴Romania vasike Ramnicu Valcea

    about UI
    i think we have the follow up in the related issues ...
    https://www.drupal.org/project/drupal/issues/3053449 Style removal button for multi-value field widget items Active
    where it seems there is plan for it ...

    or there still something to be done here?

  • last update 12 months ago
    29,757 pass, 14 fail
  • 🇫🇮Finland lauriii Finland

    I think all we need to do is move the remove button to its own column. We can handle rest of the styling in the follow-up. I pushed a commit which does this.

  • Status changed to RTBC 12 months ago
  • 🇺🇸United States smustgrave

    Works like a charm.

  • last update 12 months ago
    Build Successful
  • last update 12 months ago
    Build Successful
  • last update 12 months ago
    29,797 pass
  • 🇫🇮Finland lauriii Finland

    The test failure is due to broken HEAD.

  • last update 12 months ago
    29,802 pass
    • lauriii committed be46dafb on 11.x
      Issue #1038316 by tacituseu, vasike, heddn, Eyal Shalev, cchoe1, lauriii...
  • Status changed to Fixed 12 months ago
  • 🇫🇮Finland lauriii Finland

    Committed be46daf and pushed to 11.x. Thanks!

  • 🇺🇸United States neclimdul Houston, TX

    Exciting! This is something my users have been asking for for a long time! Is this something we'll see in D10?

  • 🇦🇺Australia mstrelan

    @neclimdul this will be in 10.2, see https://www.drupal.org/about/core/blog/new-drupal-core-branching-scheme-... for explanation of the 11.x branch

  • 🇺🇸United States neclimdul Houston, TX

    right right.. that's still confusing.

    Fixing my org credits.

  • Automatically closed - issue fixed for 2 weeks with no activity.

  • Status changed to Fixed 11 months ago
  • 🇦🇺Australia mstrelan

    Opened a follow-up bug 🐛 Removed values in multi-value fields reappear Fixed .

  • Add "Delete" Button to Each Entry:
    For each entry in the list, add a "delete" or "remove" button that users can click to remove that specific item. You can use HTML and JavaScript (or jQuery) to achieve this functionality.

    For example, your widget template could look something like this:

    Field 1:

    Field 2:

    Field 3:

    Remove

    Add JavaScript for Removing Entries:
    Write JavaScript (or jQuery) to handle the removal of entries when users click the "delete" or "remove" button. You'll need to target the button click event and remove the corresponding entry from the list.
    $(document).ready(function() {
    $(".remove-entry").on("click", function() {
    $(this).closest(".multiple-entry").remove();
    });
    });

    Handle Empty Entries on Submission:
    When handling form submissions on the server-side, you'll need to account for empty entries and remove them before saving the data to separate columns in the database.

    For example, in your form submission handler:
    $field_1_values = $form_state->getValue('field_1');
    $field_2_values = $form_state->getValue('field_2');
    $field_3_values = $form_state->getValue('field_3');

    // Remove empty entries before saving to database.
    $entries = [];
    foreach ($field_1_values as $key => $value) {
    if (!empty($value)) {
    $entries[] = [
    'field_1' => $value,
    'field_2' => $field_2_values[$key],
    'field_3' => $field_3_values[$key],
    ];
    }
    }

    // Save $entries to separate columns in the database.
    // ...

  • 🇨🇦Canada mahde Vancouver

    This fix is causing an issue with nested paragraph as it show the "Order" <td> when there is no paragraph value is added.
    See the screenshot below:

  • 🇨🇦Canada nicole.harnish Halifax

    I am having the same issue as mahde.

    Running Drupal 10.2.2

    Debugging theme.inc it looks like there are 3 headers to 4 cells, causing an extra

  • 🇬🇧United Kingdom lexsoft London

    Could someone please let me know how to disable this feature? From what I see, it does not have any UI control, and it's unreasonable to deploy something like this enabled.

    Wouldn't the best practice be to deploy it disabled initially, with a checkbox on the field config to enable it? If we have multiple fields with, let's say, 5 entries already displayed, with data present but not visible to the user, giving them the ability to remove it could lead to serious issues.

  • Form elements can already be disabled, using the disabled or readonly attributes. We don't need to add a config option for this.

  • Also, this issue is closed. If someone has a bug, it should probably be opened as a new issue in the queue.

  • 🇺🇸United States bkosborne New Jersey, USA

    Surprised a change record wasn't added for this. Not that it's super disruptive, but admin theme maintainers may have wanted to know about this so they could see how the remove button affected styling of widgets. It also would have allowed me to discover this was a new feature in Drupal 10.2.

  • 🇺🇸United States robpowell Boston

    Just echoing some of the concerns raised here, having a CR would have been helpful to know about this change quicker. Having this off by default and configurable in the UI would be beneficial incase there are clashes with custom code or contrib like multiple_fields_remove_button.

  • I agree, a change record or at least having it listed in whichever 10.2.x release it actually landed in would have been super helpful. This change was unexpected, and I do (and did) read the release pages to look for new features and other items of interest. After finding the problem in my app (as someone else suggested, there was a custom solution in place), I literally searched every 10.2.x release page (all 10 of them, at time of writing) for "remove", "multi", and (once I found it) this issue number "1038316" - and there were no references to this change that I could see. I only found this issue by searching core code for part of the resulting HTML, googling based on what I found, and following a link in a contrib issue (for a module I'm not using) that was in the search results. Can/should this issue be retroactively added to whichever 10.2.x release page it belongs to?

Production build 0.69.0 2024