I've created custom ajax powered select box. When there is an Image widget in this form, selecting multiple values, causes Illegal choice error. What is wrong?

/**
 * Implement hook_form_alter().
 */
function mymodule_form_alter(&$form, &$form_state, $form_id) {
  if (isset($form['#entity_type']) && isset($form['#bundle'])) { // entity form

    $form['items'] = array(
      '#type' => 'select',
      '#options' => array(
        t('Item 1'), t('Item 2')
      ),
      '#multiple' => TRUE,
      '#ajax' => array(
        'callback' => 'mymodule_update_subitems',
        'wrapper' => 'mymodule-subitems-field-wrapper',
        'effect' => 'fade',
        'speed' => 'fast',
      ),
    );

    $form['subitems'] = array(
      '#type' => 'item',
      '#markup' => 'Just for dev',
      '#prefix' => '<div id="mymodule-subitems-field-wrapper">',
      '#suffix' => '</div>',
    );

  }
}

function mymodule_update_subitems($form, $form_state) {
  return $form['subitems'];
}
CommentFileSizeAuthor
#36 drupal-1045208-31-36-interdiff.txt1.01 KBRoSk0
#36 drupal-1045208-36.patch845 bytesRoSk0
#31 drupal-multiselect_select_form_ajax-1045208-31.patch833 bytesmkolar
PASSED: [[SimpleTest]]: [MySQL] 41,594 pass(es). View
#24 drupal-multiselect_select_form_ajax-1045208-24.patch786 bytesmoonray
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch drupal-multiselect_select_form_ajax-1045208-24.patch. Unable to apply patch. See the log in the details link for more information. View
#5 mymodule.zip1.92 KBElaman
#1 Capture.PNG14.01 KBElaman
Members fund testing for the Drupal project. Drupal Association Learn more

Comments

Elaman’s picture

FileSize
14.01 KB

Error message screenshot.

rfay’s picture

Category: bug » support

My bet is that with multiple values selected, the ID is no longer unique. It also may be that you're affecting more form items than you expect.

Elaman’s picture

As i wrote before, if there is no Image widget in altered form, there is no error. How it can be?

rfay’s picture

@Peritus, I'm interested in the problem. If you'll package it up as a module and attach it to the issue I'll take a shot at debugging it. Please also provide the content type that gives you trouble (as a feature or export) and step-by-step instructions on how to recreate the problem.

Elaman’s picture

FileSize
1.92 KB

Thanks for response. Here is the steps to recreate problem:

  1. Install Drupal 7.0 with standard profile
  2. Create content type. For example: "Photo".
  3. Add a new field. Field type & widget: "Image".
  4. Install attached module "My Module".
  5. Go to "Photo" content type creation page. /node/add/photo
  6. Try to select two or more items in custom select-box created by module and you will get an error message.
rfay’s picture

I'll take a look. Probably this weekend.

Thanks,
-Randy

rfay’s picture

I am perfectly able to add multiple images (which is what I thought you were saying).

When I select any of the items in the select box I get

    * Warning: end() expects parameter 1 to be array, null given in file_managed_file_validate() (line 536 of /home/rfay/workspace/d7git/modules/file/file.module).
    * Warning: end() expects parameter 1 to be array, null given in file_managed_file_validate() (line 536 of /home/rfay/workspace/d7git/modules/file/file.module).
    * Warning: end() expects parameter 1 to be array, null given in file_managed_file_validate() (line 536 of /home/rfay/workspace/d7git/modules/file/file.module).

Edit: I am completely able to select multiple items in the select added by this module, and submit the results, without a validation error.

rfay’s picture

Category: support » bug

There is a problem in core here, but your problem still exists when it's fixed. I opened #1049462: Usage of deprecated $form_state['clicked_button'] causes bugs during AJAX submissions by non-buttons for that problem.


$clicked_button = end($form_state['triggering_element']['#parents']);

rfay’s picture

Title: Selecting multiple values in ajax-ed select box causes Illegal choice error » Form altering of node form doesn't happen with multiselect select form and #ajax

I agree with you that this a bug.

Here's what happens:

  • When an filefield of any type (image, file) is present (any cardinality), the form alter never gets called.
  • The validation function finds values that shouldn't be there in $form_state, and therefore the post fails basic validation.

Fields other than filefields don't seem to have this problem, as far as I can tell.

This is not fundamentally a problem with multiselect and ajax. I tried adding #multiple => TRUE to the "Generate Checkboxes" example in the AJAX Example, of course it means that the resultant value becomes an array, messing up the logic, but it works with a little alteration.

This is pretty much an edge case, but it is probably a sign of something wrong in FAPI.

rfay’s picture

I asked effulgentsia to take a look at this as well.

rfay’s picture

Update: git bisect says that the validation fail part of this was introduced in #995854: #ajax doesn't work at all if a file element (or enctype => "multipart/form-data") is included in the form on December 23. However, I don't think it actually worked before; the form-alter didn't seem to happen; but the validation failure didn't occur.

Elaman’s picture

Warning: end() expects parameter 1 to be array, null given in file_managed_file_validate() (line 536 of B:\home\dev\7.module\modules\file\file.module).

I've no error after applied #6 patch from #990260: create a new book from non-book content type gives "Passed variable is not an array..." error. issue.

But still remains Illegal choice error on multiple option select. If i select only one option, there is no error and everything work as it should. So where exactly bug is? File module?

rfay’s picture

Version: 7.0 » 7.x-dev

Yes, you've discovered a completely different problem, which is why this bug remains open and we'll pursue #1049462: Usage of deprecated $form_state['clicked_button'] causes bugs during AJAX submissions by non-buttons over there. (BTW, that solves the same issue as #990260: create a new book from non-book content type gives "Passed variable is not an array..." error., so I marked it as a dup)

Elaman’s picture

Ok. Thanks :)

effulgentsia’s picture

subscribing to look at later

fp’s picture

Ran into this issue too. Subscribing.

dscutaru’s picture

Ran into this issue too,
It seems this happens only when form contains multiple select element and file/image element(s)
If I replace the select with checkboxes it works fine - we can call this as a workaround

After some digging I found the problem being in the misc/ajax.js in the Drupal.ajax.prototype.beforeSend, aprox line 351
there is a comment and the trouble code

    // The triggering element is about to be disabled (see below), but if it
    // contains a value (e.g., a checkbox, textfield, select, etc.), ensure that
    // value is included in the submission. As per above, submissions that use
    // $.ajax() are already serialized prior to the element being disabled, so
    // this is only needed for IFRAME submissions.
    var v = $.fieldValue(this.element);
    if (v !== null) {
      options.extraData[this.element.name] = v;
    }

in the case of multiple select v is an Array, this array later in misc/jquery.form.js is used to create a hidden form element and at this point it is forced into a string, so for example [1,2,3] became "1, 2, 3"

In the end you end up with one string value in the $form_state['values'], instead of an array, for Ex:
$form_state['values']['field_cat']['und'][0][tid] = '2,4,7';
instead of
$form_state['values']['field_cat']['und'][0][tid] = '2';
$form_state['values']['field_cat']['und'][1][tid] = '4';
$form_state['values']['field_cat']['und'][2][tid] = '7';

If somehow in 2015 someone will also run into this issue and will need a solution here is mine:
replace

    if (v !== null) {
      options.extraData[this.element.name] = v;
    }

with

    if (typeof v == "object") {
      var name = this.element.name;
      $.each(v, function(i, value) {
        var key = name.replace(/\[\]/, '[' + value + ']');
        options.extraData[key] = value;
      });
    }
    else if (v !== null) {
      options.extraData[this.element.name] = v;
    }
bneel’s picture

#17 works but break views (7.x-3.7).
You need to replace

 if (v !== null) {
      options.extraData[this.element.name] = v;
    }

with

    if (v !== null && typeof v == "object") {
      var name = this.element.name;
      $.each(v, function(i, value) {
        var key = name.replace(/\[\]/, '[' + value + ']');
        options.extraData[key] = value;
      });
    }
    else if (v !== null) {
      options.extraData[this.element.name] = v;
    }

thanks @dscutaru
The diff between your suggestion is :
if (v !== null && typeof v == "object") instead of if (typeof v == "object")

brendanrjohn’s picture

did anyone else test this? I tried #18, but it seems to stop all ajax on the site. And reversing didn't fix.

brendanrjohn’s picture

ok fixed my own probs by using a backup of the ajax.js file.

koppie’s picture

Issue summary: View changes

Neither #17 nor #18 worked for me. Ajax still worked, but I still got the "illegal choice" error.

moonray’s picture

Status: Active » Reviewed & tested by the community

My use case is an entity reference multi-select field and any additional multi-value field. First add a value to the entity reference field. You then click on the 'add another item' on that additional field twice; the second time gives the illegal choice error.

#18 fixed the issue for me. No more illegal choice errors.

moonray’s picture

Status: Reviewed & tested by the community » Needs work

Setting back to 'needs work' since we don't have an actual patch file that can be tested.

moonray’s picture

Status: Needs work » Needs review
FileSize
786 bytes
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch drupal-multiselect_select_form_ajax-1045208-24.patch. Unable to apply patch. See the log in the details link for more information. View

And an actual patch based on #18.

Status: Needs review » Needs work

The last submitted patch, 24: drupal-multiselect_select_form_ajax-1045208-24.patch, failed testing.

moonray’s picture

Did some additional testing, and it looks like the problem persists. In my case, it's an issue with entityreference module, though.

vistree’s picture

Hi, I also have this problem with entityreference. Nothing described on this page helped. Any progress?

debaryadas’s picture

Hi,

A same issue has been discussed at https://drupal.org/node/2229055 and a solution has been provided. Anyone interested can check it.

drupup’s picture

Same issue.

Setup:
Standard node add/edit form
Entity reference fields, multiselect, selection filtered by view, checkbox
File upload fields

I've been checking the logs, and it appears that as soon as a file is uploaded it breaks the view used to generate the items for the entity reference field:

Notice: Trying to get property of non-object in eval() (line 2 of .../sites/all/modules/views/plugins/views_plugin_argument_default_php.inc(53) : eval()'d code).

The "Illegal choice" notice happens because the entity reference field is now, technically speaking, "empty" despite the fact that it has multiple items selected.

If I switch the entity reference field so it is not filtered by view the problem goes away.

It doesn't matter whether there are zero, one, or multiple items selected...the view throws out the notice every time the upload field is used. So the issue is clearly with the view that filters the field.

Switching between widgets (select list, autocomplete, checkbox), as has been suggested elsewhere, has no effect. I've tried to implement #17/#18, but neither had any effect.

I'd be willing to try to implement the workaround suggested in #28, but will need better instructions about how and where to implement the code. My situation involves a standard node edit screen, not a custom form.

This is a fairly common use case. This issue queue is going to grow until this is resolved.

bartram’s picture

You can create an empty beforeSend AJAX method that won't disable the triggering element to avoid this issue:

// Get rid of Drupal's default behavior of disabling form elements before sending data
Drupal.ajax.prototype.beforeSend = function (xmlhttprequest, options) {
}
mkolar’s picture

FileSize
833 bytes
PASSED: [[SimpleTest]]: [MySQL] 41,594 pass(es). View

I have same problem with taxonomy term ref (multiselect options).. And code from #24 helped. Here is the patch for actual 7.x branch.

cilefen’s picture

Status: Needs work » Needs review
sassafrass’s picture

My scenario: I have created a custom entity. In the corresponding custom form I implemented a select_or_other element where '#multiple' => TRUE. Upon selection, ajax is used to populate a second select element.

Works as expected under the following conditions:
- Works if I don't have a file field associated with the entity/form.
- Works if I do have a file field associated with the entity/form, but I only select a single value

Doesn't work as expected when:
- I add a file field and I attempt to select more than one value

I tried the patch in #31, but it did not work for this scenario.

My scenario and results are similar to the issue discussed here: https://drupal.org/node/2229055
I implemented a similar solution in that I check the values submitted. If it has been converted to a string, I explode the string into an array and use that instead.

skadu’s picture

Patch in #31 works for me. I am using an entityreference multi select field. Without the patch, if I have more than one option selected in that list and I trigger ajax (from another field) I would get the Illegal Choice messages. However, with this patch, I am able to work within the form as expected.

happysnowmantech’s picture

Patch in #31 worked for me when I encountered this problem with an taxonomy term entityreference multi select field with reference_option_limit module enabled.

RoSk0’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
845 bytes
1.01 KB

Used this patch heavily in one of my projects, works well.

Attaching same patch with small code style fixes.

vladan.me’s picture

Status: Reviewed & tested by the community » Needs work

As mentioned in #33, it does not work for case when there's a file field and multiple select. As a matter of fact, changed piece of code doesn't even trigger in that particular case. Not quite sure how to proceed from here, any ideas?

EDIT:
Actually, it didn't trigger because other module/theme override it. In my particular case, bootstrap theme has its own ajax.js file. This patch is good to go.

vladan.me’s picture

Status: Needs work » Reviewed & tested by the community
David_Rothstein’s picture

Status: Reviewed & tested by the community » Needs review
+        options.extraData[key] = value;
+      });
+    }
+    else {
+      if (v !== null) {
+        options.extraData[this.element.name] = Drupal.checkPlain(v);

Is it really correct that the first one doesn't need to be run through Drupal.checkPlain() like the second one? If it doesn't, why not?

Also, this issue doesn't mention Drupal 8 anywhere - does the problem exist in Drupal 8 too? If so, we need to get it fixed there first.

Finally, this issue could probably use a new title and an issue summary update... it is a little unclear exactly what this issue is about, but I don't think it has anything specific to do with form altering the node form (even though that is in the current issue title).