Uploading a file, clicking "add another item", failing validation are all ways to rebuild the form.
When that happens, the unique IDs are recalculated.

This prevents the "Remove" button from working correctly, and it can cause many unrelated errors the second time it is clicked.

In my use case, field_collection_remove_js is where things break. It doesn't take drupal_html_id() into account when determining the $element['#id'].

Comments

karens’s picture

Project: Content Construction Kit (CCK) » Field collection
Version: 7.x-2.x-dev » 7.x-1.x-dev
Component: CCK in core » Code

Wrong project, moving.

andiart’s picture

The same I experience. I have own ajax functionality in my form_alter function. Maybe it collides somehow with the field collection ajax call. I for my part get an answer from the ajax call but the changes aren't reflected in the browser. Tomorrow I will examine it further.

The problem seems to occur because of a html-id in my form, which doesn't correspond to the html id of the field collection. The module clears the id's cache in it's callback function for remove:

  // drupal_html_id() very helpfully ensures that all html IDS are unique
  // on a page. Unfortunately what it doesn't realize is that the IDs
  // we are generating are going to replace IDs that already exist, so
  // this actually works against us.
  if (isset($_POST['ajax_html_ids'])) {
    unset($_POST['ajax_html_ids']);
  }

The callback trys to replace the content of a html element with id edit-field-invitee but in the html of my form the element is attributet with the an id of edit-field-invitee--2.

prateekjain’s picture

I am having the same issue, but its independent of the 'unlimited values' setting.

Let me know if you guys find the solution.

Thanks,
Prateek

Nick Robillard’s picture

Status: Active » Needs work

I too have encountered this issue and have fixed it (at least for now). The solution is somewhat of a hack. I pull out the actual id from the POST 'ajax_html_ids' array and do the ajax commands using it. Like I say in my comment, I'm sure this could be done better. I am not that familiar with core D7 ajax stuff yet and I'm in a hurry. I'm using a bastardized version of Field Collection because I require Field Collection Table support, so a patch would be kind of useless. Here's my whole function field_collection_remove_js():

<?php
function field_collection_remove_js() {
  // drupal_html_id() very helpfully ensures that all html IDS are unique
  // on a page. Unfortunately what it doesn't realize is that the IDs
  // we are generating are going to replace IDs that already exist, so
  // this actually works against us.
  $ajax_html_ids = array();
  if (isset($_POST['ajax_html_ids'])) {
    $ajax_html_ids = $_POST['ajax_html_ids'];
    unset($_POST['ajax_html_ids']);
  }

  list($form, $form_state) = ajax_get_form();
  drupal_process_form($form['#form_id'], $form, $form_state);

  // Get the information on what we're removing.
  $button = $form_state['triggering_element'];
  // Go two levels up in the form, to the whole widget.
  $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -3));
  
  // Check 'ajax_html_ids' to make sure we are using the actual id. I'm sure this could be done more cleanly.
  $actual_id = $element['#id'];
  foreach ($ajax_html_ids as $key => $id) {
    if ($id == $element['#id'] || strpos($id, $element['#id'] . '--') === 0) {
      $actual_id = $id;
      break;
    }
  }
  
  // Now send back the proper AJAX command to replace it.
  $return = array(
    '#type' => 'ajax',
    '#commands' => array(
      ajax_command_replace('#' . $actual_id, drupal_render($element))
    ),
  );

  // Because we're doing this ourselves, messages aren't automatic. We have
  // to add them.
  $messages = theme('status_messages');
  if ($messages) {
    $return['#commands'][] = ajax_command_prepend('#' . $actual_id, $messages);
  }

  return $return;
}
?>
riseman’s picture

If you get form via ajax, there is another solution.
I get hint from somewhere but i can not remember exact issue number.
but key is this.
in field_collection_remove_js function,
required inc file is not included if the parent of field collection is node type.
So, If you get a form by using ajax, you have to add required resources after calling ajax_get_form

function field_collection_remove_js() {
  // drupal_html_id() very helpfully ensures that all html IDS are unique
  // on a page. Unfortunately what it doesn't realize is that the IDs
  // we are generating are going to replace IDs that already exist, so
  // this actually works against us.
  if (isset($_POST['ajax_html_ids'])) {
    unset($_POST['ajax_html_ids']);
  }

  list($form, $form_state) = ajax_get_form();
  // Include required files to process form actions
  if ($form['#entity_type'] == 'node') {
    module_load_include('inc', 'node', 'node.pages');
  }
  
  drupal_process_form($form['#form_id'], $form, $form_state);

  // Get the information on what we're removing.
  $button = $form_state['triggering_element'];
  // Go two levels up in the form, to the whole widget.
  $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -3));
  // Now send back the proper AJAX command to replace it.
  $return = array(
    '#type' => 'ajax',
    '#commands' => array(
      ajax_command_replace('#' . $element['#id'], drupal_render($element))
    ),
  );

  // Because we're doing this ourselves, messages aren't automatic. We have
  // to add them.
  $messages = theme('status_messages');
  if ($messages) {
    $return['#commands'][] = ajax_command_prepend('#' . $element['#id'], $messages);
  }

  return $return;
}
tim.plunkett’s picture

Title: remove button in 'add another' field for unlimited values doesn't work » Remove button in 'add another' field for unlimited values breaks with ajax (field_collection_remove_js doesn't respect drupal_html_id)
Category: Support request » Bug report
Priority: Normal » Major
Issue summary: View changes
Status: Needs work » Active

#5 is unrelated to this issue, that's just because you should have used form_load_include() instead.

#4 is along the right lines, but is a brittle hack, and will not work in all cases.

tim.plunkett’s picture

Title: Remove button in 'add another' field for unlimited values breaks with ajax (field_collection_remove_js doesn't respect drupal_html_id) » Remove button broken if form is rebuilt (via ajax or failed validation)
Issue summary: View changes
Related issues: +#1305882: drupal_html_id() considered harmful; remove ajax_html_ids to use GET (not POST) AJAX requests
tim.plunkett’s picture

Status: Active » Needs review
StatusFileSize
new1.73 KB

This is equally hacky, but also works for AJAX as well as failed validation.
Because of #1575060: ajax_html_ids are broken for forms with file element (encoding=multipart/form-data), the structure of $_POST['ajax_html_ids'] is variable.

Basically, unless drupal_html_id is not used on forms, or unless *every AJAX callback* uses the unset($_POST['ajax_html_ids']) hack, we need to code around this somehow.

tim.plunkett’s picture

For my use case, #1315900: Ctools Modal form's #id changes when validation fails actually solves the problem as well.

We likely need some core documentation that any AJAX callback that is doing form processing must unset the ajax_html_ids first.

jmuzz’s picture

Status: Needs review » Postponed (maintainer needs more info)

I made a content type with a field collection (unlimited cardinality) that contains a text field and a file field. The content type also has its own file field.

I created a node with a bunch of field collection items with sequential values in their text fields. I edited it several times trying things like adding items and uploading files and then deleting some field collection items and then submitting the form. I didn't see anything strange happening.

What exactly is the problem behavior and how can it be duplicated?

iLLin’s picture

Status: Postponed (maintainer needs more info) » Needs review

To duplicate, you need to make your form AJAX. Select the add button to add another one of the fields and they press submit with a validation error. In your AJAX callback, return the complete form. Now try to remove one of your fields with the remove button. It will fail.

I have applied the patch and my form is now working as expected.

Works for me, Thanks!

dewalt’s picture

I propose patch based on using Drupal AJAX API.

It fills proper element id in 'wrapper' key of element '#ajax' property. AJAX callback was changed from page callback to ajax callback, using function field_add_more_js() as example for callback.

estoyausente’s picture

I have a problem removing fc items but I'm not sure if is exactly the same. For any reason, the remove button don't work and I don't know exactly why. I searched and I found several issues related with field collection removing proccess.

@dewalt This patch is work for me and resolve my problem, but like I said, I'm not sure if is the same problem, a similar problem or if it's completely different.

but... really thanks!!!!! :-)

rlmumford’s picture

Status: Needs review » Needs work

I have what might be a different problem (that I thought might be related) where the remove button always removes the last item from the field, regardless of which row you click remove on. Applying this patch broke the module even further so that now none of the buttons do anything.

dewalt’s picture

@rlmumford - As I understood, the problem is:

the remove button always removes the last item from the field, regardless of which row you click remove on.

This works correctly on my project, where the patch used.

Can you please provide more details, about module broke with patch? What version of module you patched? Were additional modules installed, that depend on field_collection?

rlmumford’s picture

vtkachenko’s picture

Patch #12 works for me.

ybabel’s picture

Patch #12 worked for me

balintcsaba’s picture

Patch #12 worked for me to

matysek145’s picture

Patch #12 worked for me to

estoyausente’s picture

Status: Needs review » Reviewed & tested by the community

I think that it's RBTC, 4 reviewer is enough.

nancydru’s picture

Patch works for me; the issue in #15 did not happen on my site. Please commit.

roopeshnaik’s picture

I need this for beta5 module, here is the patch

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 23: field_collection-remove_button_broken-2549343-1.patch, failed testing.

roopeshnaik’s picture

StatusFileSize
new4.91 KB

Update the patch file name

dewalt’s picture

Re-uploading working patch.

dewalt’s picture

Status: Needs work » Reviewed & tested by the community

  • dewalt authored d797efa on 7.x-1.x
    Issue #1675522 by tim.plunkett, dewalt: Remove button broken if form is...
jmuzz’s picture

Status: Reviewed & tested by the community » Fixed

Thanks!

Status: Fixed » Closed (fixed)

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

brendarossi’s picture

Still experiencing problems with AJAX deletion, this code patches 7.x-1.0-beta11.