To reproduce this problem:

  • Create a new form
  • Add an element to the form with '#element_validate' => some_function
  • Set the #limit_validation_errors to array() (meaning you want to limit ALL validation)
  • Now submit the form

.

The validation function runs; the expected behavior is that these functions would not be run, no? It should be noted that errors set by form_error are not displayed (which is good), but this is still a problem when the validation function in question sets (or unsets) some values in $form_state. In addition, these see like wasteful function calls to me... am I missing something?

Comments

jaypan’s picture

I'm seeing this in D7 as well.

jaypan’s picture

Version: 8.x-dev » 7.15
Priority: Normal » Major
bleen’s picture

Version: 7.15 » 8.x-dev
Priority: Major » Normal

@Jaypan - Bugs like this always need to be fixed in the latest dev version (in this case 8.x) before it can be back-ported to previous versions. Also, while this bug is tremendously annoying, it does not really meet the definition of major outlined here: http://drupal.org/node/45111

jaypan’s picture

Fair enough. Thank you for setting it correctly.

drifter’s picture

[never mind, didn't read the issue carefully, mine was about a different edge case]

valthebald’s picture

Priority: Normal » Major
Issue tags: +Needs backport to D7

If you have a form with required element, and dependent select boxes (i.e. Country/State), population via #ajax won't work (because form validation breaks on required element).
There are hacks to overcome that, but of course current behavior is annoying.
IMO this can qualify this issue as 'major'

tim.plunkett’s picture

Priority: Major » Normal

This does not "have significant repercussions", it "affect[s] one piece of functionality are normal priority".
See the handbook page linked to in #3.

jaypan’s picture

I never actually read the page that was linked to until now, but looking at it, for an error marked 'major', the handbook states:

Issues which have significant repercussions but do not render the whole system unusable are marked major. An example would be a PHP error which is only triggered under rare circumstances or which affects only a small percentage of all users.

For me it has quite significant repercussions. I have a form that is essentially the major focal point for the system I'm currently building. This issue was causing errors to popup every time the form was used (I had to hack a way around it). The fact that it is a Drupal error and not a PHP error is a minor point in my mind, it still affects the functionality of the system as a whole for my users, even though it doesn't render the whole system unusable.

As such, I'm actually of the opinion that my original status of 'major' was in fact correct. I'm just a single person though, so I will not change the status of this Issue. But I feel that marking as normal is in fact incorrect and is doing a disservice to anyone who may be using a 3rd party module that has #element_validate set, and can only fix this issue by hacking the module code or core.

valthebald’s picture

Assigned: Unassigned » valthebald
Issue tags: +Needs tests

I want to keep this issue.
Also, I think it's worth adding a test to prevent wrong behavior in the future

bleen’s picture

by "keep this issue" do you mean you are planning on working on it and submitting a patch? If so, awesome!! if not, please assign it back to Anonymous :)

valthebald’s picture

Working on the patch already...

valthebald’s picture

Status: Active » Closed (works as designed)

TL;DR - issue does not exist, add '#button_type' to the calling select box

Long version:
I've started to build test case for D8 to simulate problematic behavior, and quickly found, that it's not reproducible.
I've digged into dark magic of form.inc, and found the following (starting from line 1380 in D8, same exists in D7 branch):

    // If submit handlers won't run (due to the submission having been triggered
    // by an element whose #executes_submit_callback property isn't TRUE), then
    // it's safe to suppress all validation errors, and we do so by default,
    // which is particularly useful during an Ajax submission triggered by a
    // non-button. An element can override this default by setting the
    // #limit_validation_errors property. For button element types,
    // #limit_validation_errors defaults to FALSE (via system_element_info()),
    // so that full validation is their default behavior.
    elseif (isset($form_state['triggering_element']) && !isset($form_state['triggering_element']['#limit_validation_errors']) && !$form_state['submitted']) {
      form_set_error(NULL, '', array());
    }

Another issue is related to form_builder() function.
If the logic that populates dependent select box values, looks into $form_state['values'], but not $form_state['input'] (it should do so, because $form_state['values'] are safer), it may found empty value. Reason is located in form_builder(), starting from line 1175:

if (isset($form_state['triggering_element']['#button_type'])) {
...
     $name = $form_state['triggering_element']['#name'];
      if (!isset($values[$name]) && isset($form_state['values'][$name]) && $form_state['values'][$name] === $button_value) {
        $values[$name] = $button_value;
      }
}

So, there are good chances triggering element's value just doesn't find it's way to $form_state['values']

After some more investigation, I found
a) original issue #684846: AJAX triggered by non-submit element fails if any elements are validated and
b) chx's blog post at http://www.drupal4hu.com/node/286

Example code that checked and works both with 8.x and 7.x:

form definition:

function mymodule_myform($form, &$form_state) {
  $form['select_master'] = array(
    '#title' => t('AJAX-enabled selectbox.'),
    '#description' => t("When this one AJAX-triggers and the spare required field is empty, you should not get an error."), 
    '#type' => 'select',
    '#options' => array('-' => 'None', '1' => 'Option 1', '2' => 'Option 2'), 
    '#default_value' => !empty($form_state['values']['select_master']) ? $form_state['values']['select_master'] : array(),
    '#button_type' => 'select',
    '#ajax' => array(
      'event' => 'change',
      'callback' => 'mymodule_ajax_callback',
      'wrapper' => 'select_dependant',
      'method' => 'replace'),
  );

  if (empty($form_state['values']['select_master'])) {
    $dependant_options = array('0' => 'Default value');
  }
  elseif ($form_state['values']['select_master'] == '1') {
    $dependant_options = array('0' => 'Empty', '1' => 'First value');
  }
  else {
    $dependant_options = array('0' => 'Empty', '1' => 'First value', '2' => 'Second value');
  }
  $form['select_dependant'] = array(
    '#title' => t('Dependant selectbox'),
    '#prefix' => '<div id="select_dependant">',
    '#suffix' => '</div>',
    '#description' => t("Options of this selectbox must be populated upon change in previous select box."),
    '#type' => 'select',
    '#options' => $dependant_options,
  );

And AJAX callback:

function mymodule_ajax_callback($form, $form_state) {
  return $form['select_dependant'];
}
jaypan’s picture

Version: 8.x-dev » 7.9
Status: Closed (works as designed) » Active

This is a very real problem that I just faced yesterday. If 'works as designed' means that it's designed not to work, then you are correct. But right validation is being called on elements with #element_validate, even when #limit_validation_errors is set to an empty array(). Maybe it's been fixed for D8, but in D7, the problem exists.

jaypan’s picture

Version: 8.x-dev » 7.9

I should add, in your example you didn't use #limit_validation_errors.

bleen’s picture

Version: 7.9 » 8.x-dev

Please follow the steps to reproduce in the original post:

  • Create a new form
  • Add an element to the form with '#element_validate' => some_function
  • Set the #limit_validation_errors to array() (meaning you want to limit ALL validation)
  • Now submit the form

"some_function" should not be called in this case, but it is (which is a bug)

Following these steps *does* produce the error as reported.

valthebald’s picture

Version: 7.9 » 8.x-dev

Do you mean that the same element has #element_validate and empty #limit_validation_errors? What's the use case for that?

bleen’s picture

#16: no ...

  • Create a new form
  • Add a text element called FOO to the form with '#element_validate' => some_function_foo_validate
  • Add a submit button (typically this fires off an AJAX callback) called BAR with #limit_validation_errors to array() (meaning you want to limit ALL validation)
  • Now submit the form
valthebald’s picture

From Form API documentation:

#limit_validation_errors does not have any effect if #submit is not set. More discussion is in the form_set_error() documentation.

Could you add #submit and retry?

bleen’s picture

Same...

  • Create a new form with a custom submit handler indicated by using #submit
  • Add a text element called FOO to the form with '#element_validate' => some_function_foo_validate
  • Add a submit button (typically this fires off an AJAX callback) called BAR with #limit_validation_errors to array() (meaning you want to limit ALL validation)
  • Now submit the form

... I'm not sure why you are persisting with such vigor to show that this is not an error. It is, and it has been confirmed by several people. If you can concretely show that this is "as designed" please provide some example code. I'd love to look at something like that so we can have a real discussion about this issue being closed or so that we can get past the doubts and move towards coming up with a fix...

valthebald’s picture

#19:

Now submit the form

Since you have different submit elements, which one is clicked?

bleen’s picture

the one with the #limit_validation_errors

valthebald’s picture

StatusFileSize
new1.03 KB
new1015 bytes

I've attached proof of why current behavior does not contain bug mentioned in this issue (should be saved as example.module, URL to test is example_menu_item
Please note that in button declaration

  $form['button'] = array(
    '#type' => 'submit',
    '#value' => 'Submit',
    '#submit' => array('example_button_submit'),
    '#limit_validation_errors' => array(),
  );

both #submit and #limit_validation_errors are mandatory.
You can comment out any of these line and see that form will start returning validation errors

jaypan’s picture

And how is that not a bug?

valthebald’s picture

#23: The code attached works exactly as it explained in API documentation.
Where's the bug?

bleen’s picture

StatusFileSize
new2.44 KB

Ok ... I can confirm that the sample module in #22 works as designed. It appears that this issue is a bit more complex and it is related to using Ajax to add a new form element that has a validation function. Attached is a sample module that demonstrates this error. To reproduce, install the attached module and go to mysite.com/test-menu-item , fill in the text field and click the "Add another text field" button.

Error is seen...

bleen’s picture

... the module in #25 is for D7

bleen’s picture

...also, thanks valthebald for putting some code on paper so we can finally move this forward

chanderbhushan’s picture

<?php

function modulename_menu() {
$items['example_menu_item'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array('my_form'),
'access arguments' => array('access content'),
);
return $items;
}

function my_form($form, &$form_state) {
$form['test'] = array(
'#type' => 'textfield',
'#title' => 'Text field',
'#element_validate' => array('test_element_validate'),
);
$form['button'] = array(
'#type' => 'submit',
'#value' => 'Submit',
'#submit' => array('test_button_submit'),
'#limit_validation_errors' => array(),
);
$form['#submit'] = array('test_submit_handler');
return $form;
}

function test_submit_handler($form, &$form_state) {
drupal_set_message('Submit callback called');
}

function test_button_submit($form, &$form_state) {
drupal_set_message('Button callback called');
}

function test_element_validate($form, &$form_state) {
form_set_error('text_el', 'This element never validates');
}

valthebald’s picture

StatusFileSize
new1.34 KB

I guess we are slowly entering area of Form API dark magic...
First, module attached to #25 not exactly serves it's purpose (by the way, same code works for D8 as well)
It doesn't add new text fields, even when error messages are off.
Attached please find slightly changed module that does the trick

You can uncomment line 68:

  // drupal_set_message('THERE IS A BUG: Element validation called but it shouldnt be', 'error');

to see that element validate callbacks are called.
Additional textboxes appear with or without drupal_set_message() call.
Commenting line 42:

    '#limit_validation_errors' => array(),

leads to additional textboxes not appearing, and form error.

Conclusion: #element_validate handlers are called always. #limit_validation_errors let us ignore validation results and proceed to submit callbacks.

Is that a bug? I would say no, because I can imagine several reasons why such behavior could be developed on purpose.
Probably we can ask one of the Form API maintainers for confirmation

bleen’s picture

Conclusion: #element_validate handlers are called always. #limit_validation_errors let us ignore validation results and proceed to submit callbacks

This behavior is precisely the problem. AJAX actions will do a complete form submission but in that case it is often very very bad for the validation to run at all.

valthebald’s picture

Classical "bug or feature" dilemma. I can give good reasons to always run validate callbacks, you see it as a bug. Obviously we need a third opinion here

andypost’s picture

Suppose this issue requires @chx review

valthebald’s picture

Status: Active » Closed (works as designed)
Issue tags: -Needs tests, -Needs backport to D7

@chx's answer (from IRC):

it's not a bug
the key says 'limit validation errors'
so what happens is that we supress form_error
validation still happens , not just #element_validate
but we do not store them in form_error
it'd be called limit_validation if that'd be the intent

nerdoc’s picture

Issue summary: View changes
agence web coheractio’s picture

I face the same situation as described in #30 :

  • Radio buttons with #required=TRUE and no default value set
  • Submit button with an ajax callback and #limit_validation_errors = []

When I click on the submit, the submit process is blocked by an "illegal choice" error set on the radio buttons.

How can I prevent this validation to happen?

For the moment, the only solution I've found is to write a form validation function that gets rid of this error.

Thanks

Laurent

jaypan’s picture

If you’re seeing the illegal choice detected error, it is a different issue to this one. I’m guessing you’ve added form elements in an Ajax callback.

agence web coheractio’s picture

That's the same issue as described in #30

1. Add radio buttons with ajax submit (these radio buttons are set to #required=TRUE)
2. Remove these radio buttons with ajax submit having #limit_validation_errors = []
3. Form gets stuck with "illegal choice" error set on the radio buttons.

In my opinion, this shouldn't be the case : radio buttons shall be removed without form state error.

If this is not bug (as mentionned in #33), I struggle to understand the rationale behind this behavior?

jaypan’s picture

My last comment remains. You’re misdiagnosing the issue.

agence web coheractio’s picture

You should read before commenting. It always helps...

The bug (or whatever it is called) I describe is precisely caused by the fact that :

#element_validate functions are still called even when '#limit_validation_errors' => array()

which is the title of this issue.

The side effect of that calls is that errors detected during the elements validation (e.g. a required element not set) are preventing to pass the validation process even though they should not be taken into account because of the #limit_validation_errors = [] set on the submit element .

Anyway, I've found a solution on my own to circumvent this "Closed (works as designed)" issue or feature request (or whatever it is called) .

jaypan’s picture

You should read before commenting. It always helps...

I did:

1. Add radio buttons with ajax submit (these radio buttons are set to #required=TRUE)

Adding/removing/altering elements in Ajax callbacks causes the error you are seeing. You’re misdiagnosing the issue. It’s not related to this topic.