Problem/Motivation

When using Form API states, 'checked' and 'unchecked' cannot be applied to checkboxes elements.

Proposed resolution

Duplicate the method that is already used to apply 'disabled' with states: search for the closest element to the target with a form item or wrapper class, and change the attribute on all of its children.

Remaining tasks

  1. Write tests
  2. Fix bug
  3. Reviews/refinements.
  4. RTBC.
  5. Commit to 10.1.x
  6. Cherry pick commit fdb1fa9ff0 from 10.1.x to 10.0.x branch.
  7. Commit patch #106 to 9.5.x branch.

Original report by Cyberwolf

I am trying to use #states => array( 'enabled' => ... ) or #states => array( 'disabled' => ... ) on form elements of #type 'radios' or 'checkboxes'.

When the state change conditions apply, the disabled attribute gets set on a div around the radio buttons / checkboxes. The radio buttons / checkboxes themselves are not enabled or disabled.

CommentFileSizeAuthor
#106 994360.100_106.interdiff.txt2.28 KBdww
#106 994360-106.9_5_x.patch17.04 KBdww
#100 interdiff.txt2.84 KBlauriii
#100 994360-100.patch16.02 KBlauriii
#96 994360.93_96.interdiff.txt13.91 KBdww
#96 994360-96.patch13.84 KBdww
#96 994360-96.test-only.patch13.1 KBdww
#93 994360-93.patch2.08 KBAbhisheksingh27
#92 994360_92.patch3.11 KBreszli
#91 994360-nr-bot.txt143 bytesneeds-review-queue-bot
#85 994360_85.patch3.04 KBvsujeetkumar
#74 drupal-interdiff-994360-71-74.txt772 bytesgapple
#74 drupal-994360-74-states-checkboxes-checked.patch2.94 KBgapple
#71 drupal-994360-71-states-checkboxes-checked.patch2.33 KBgapple
#70 drupal-994360-70-states-checkboxes-checked.patch1.61 KBgapple
#69 drupal-994360-69-states-checkboxes-checked.patch1.68 KBgapple
#51 state.patch921 bytesdroplet
#50 issue_test.zip1.85 KBesclapes
#40 radio_after.png17.57 KBtimisoreana
#40 checkbox_after.png25.99 KBtimisoreana
#40 radio_before.png18.14 KBtimisoreana
#40 checkbox_before.png24.19 KBtimisoreana
#39 issue_test.zip1.44 KBandypost
#39 states_cannot-994360-39.patch347 bytesandypost
#36 drupal-8.x-fix_states_disabled_checked-994360-36.patch347 bytesDuaelFr
#36 drupal-7.x-fix_states_disabled_checked-994360-36-do-not-test.patch364 bytesDuaelFr
#31 drupal-8.x-fix_states_disabled_checked-994360-31.patch384 bytesDuaelFr
#31 drupal-7.x-fix_states_disabled_checked-994360-31-do-not-test.patch364 bytesDuaelFr
#30 drupal-7.x-fix_states_disabled_checked-994360-30-do-not-test.patch773 bytesDuaelFr
#30 drupal-8.x-fix_states_disabled_checked-994360-30.patch829 bytesDuaelFr
#21 drupal-states_bug-994360-0.patch914 bytesemosbaugh
#10 issue_994360.tar_.gz738 bytesCyberwolf
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

aspilicious’s picture

Status: Active » Postponed (maintainer needs more info)

Is this still an issue? I used states before (even with checkboxes) had no problem.

wiifm’s picture

Status: Postponed (maintainer needs more info) » Active

Still definitely an issue,

Running drupal 7.8 and I am looking to check a checkbox and disable it when another checkbox is ticked.

This is a snippet of the code:

$form['field_guides_tools_a_z']['#states'] = array(
  'checked' => array(
    '#edit-field-guides-tools-menu-und' => array('checked' => TRUE),
  ),
  'disabled' => array(
    '#edit-field-guides-tools-menu-und' => array('checked' => TRUE),
  ),
);

The jQuery looks as though it works, but the issue lies in the fact that the 'checked' and 'disabled' attributes get applied to the parent div rather than the input element. Here is the resulting HTML for the above form element, after the dependent checkbox is checked.

<div id="edit-field-guides-tools-a-z" class="field-type-list-boolean field-name-field-guides-tools-a-z field-widget-options-onoff form-wrapper" checked="true" disabled="true"><div class="form-item form-type-checkbox form-item-field-guides-tools-a-z-und">
 <input type="checkbox" class="form-checkbox" value="1" name="field_guides_tools_a_z[und]" id="edit-field-guides-tools-a-z-und">  <label for="edit-field-guides-tools-a-z-und" class="option">Show in Guides &amp; Tools Full A-Z </label>

<div class="description">Check this to show in the Guides &amp; Tools Full A-Z</div>
</div>
</div>

This must be a bug in core, anyone have a patch or workaround for this?

acbramley’s picture

Can confirm the behaviour wiifm has described above.

wiifm’s picture

Issue tags: +FAPI #states

added tag

emosbaugh’s picture

I believe that the correct code should be as follows. This is because field_guides_tools_a_z when used with the field API is of field type "container." Does that solve your problem?

  $form['field_guides_tools_a_z']['und']['#states'] = array(
    'checked' => array(
      '#edit-field-guides-tools-menu-und' => array('checked' => TRUE),
    ),
    'disabled' => array(
      '#edit-field-guides-tools-menu-und' => array('checked' => TRUE),
    ),
  );
acbramley’s picture

@emosbaugh Wow thanks that fixed it! That's really strange though because we are also using the "visible" state for another checkbox and that works without the language key in there too. Could this be a bug?

acbramley’s picture

This should probably be documented somewhere as well, we tried finding a fix for this for hours and didn't get any hint that we needed to add the language key to the array.

emosbaugh’s picture

Status: Active » Closed (works as designed)
chx’s picture

Version: 7.0-rc1 » 7.x-dev
Component: forms system » documentation
Status: Closed (works as designed) » Active

Why was this closed down without a link to the updated documentation?

Cyberwolf’s picture

FileSize
738 bytes

I think my description of the issue was slightly misunderstood. Uploading an example module here that demonstrates it. Just enable it on your Drupal installation and go to the path issue/994360.

jhodgdon’s picture

Cyberwolf: are you saying that the fix in #5 doesn't work for you?

damien_vancouver’s picture

The same issue exists for type=select, and #5 fixed it for me. Thank you!

Cyberwolf’s picture

@jhodgdon: my example code does not use the field API, just the form API. If somebody would be able to take a look at my example code and tell me how it should be fixed if this is really expected behavior, you would make my day :)

NROTC_Webmaster’s picture

The doc's page is http://api.drupal.org/api/examples/form_example%21form_example_states.in...

The Examples module http://drupal.org/project/examples form_example_states.inc should have real examples though.

jhodgdon’s picture

Status: Active » Postponed (maintainer needs more info)

I don't understand this issue.

Is there a problem with the documentation? If so, what documentation needs to be changed, on what documentation page, and what should it say that it is currently not saying?

Or is there a bug in the code? If so, can you describe what is happening when, vs. what should be happening?

Or is this a support request? If so, please move it to the Drupal.org forums, or just change the category of this issue to "support request" and mark it with status "fixed".

Cyberwolf’s picture

I provided a sample module showing you what goes wrong. I have no idea what else I can do to make my point clear. This is not a support request, this is a bug report.

Copy pasting some code from the sample module which illustrates the problem:

 $options = array();
  $option_range = range(1, 4);
  foreach ($option_range as $option_number) {
    $options['option' . $option_number] = t('Option !number', array('!number' => $option_number));
  }

  $form['disable_checkboxes'] = array(
    '#type' => 'checkbox',
    '#title' => t('Disable the checkboxes below'),
  );

  $form['checkboxes'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Checkboxes'),
    '#options' => $options,
    '#states' => array(
      'disabled' => array(
      	':input[name="disable_checkboxes"]' => array('checked' => TRUE),
      ),
    ),
  );

  $form['disable_radios'] = array(
      '#type' => 'checkbox',
      '#title' => t('disable the radios below'),
  );

  $form['radios'] = array(
    '#type' => 'radios',
    '#title' => t('radios'),
    '#options' => $options,
    '#states' => array(
      'disabled' => array(
      	':input[name="disable_radios"]' => array('checked' => TRUE),
      ),
    ),
  );

When the 'disable_checkboxes' checkbox input field is checked, the checkboxes should become disabled. When the 'disable_radios' checkbox input field is checked, the radios should become disabled. None of both works as expected. The disabled attribute gets set on a div around the radio buttons / checkboxes. The radio buttons / checkboxes themselves are not enabled or disabled.

damien_vancouver’s picture

@cyberwolf: Thanks for posting the code here.

I pasted it into a test form and try as I might, I was also unable to get the disabled/enabled to work. I did note that as per the form examples, "visible" does work for hiding/un-hiding groups of checkboxes. So this works fine:

  $form['checkboxes'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Checkboxes'),
    '#options' => $options,
    '#states' => array(
      'visible' => array(
      	':input[name="disable_checkboxes"]' => array('checked' => FALSE),
      ),
    ),
  );

and the checkboxes hide or re-appear based on the "disable checkboxes" box.

The issue is that you can't use 'enabled' and 'disabled' on type=checkboxes or type=radios. That is because these elements are simply wrapper divs in the HTML output, and so the disabled attribute gets set on the wrapper div itself, not the individual type=checkbox sub-items that are the checkboxes. Since those checkbox items do not get a disabled=true on them, they keep working. Using visible=false on the other hand, hides the entire contents of the outer div - so that works as one would expect.

In hopes I could make this work like it seems to for Field API fields (using hook_form_alter), I tried adding the LANGUAGE_NONE key ('und') in several places. I tried wrapping the checkboxes element inside a container element and setting #states on that (trying to replicate how field API seems to do it), but no luck. I tried setting the #states in hook_form_alter but still no luck.

My conclusion after seeing what is happening with disabled=true is that for it work like you are hoping we'd need improvements to misc/states.js, so that it was smart enough to figure out that it's been called on a type=checkboxes or type=radios wrapper div, and in that case apply the #states logic to each sub-element instead of/as well as the outer div. (so all four type=checkbox elements, :input[name="checkboxes[option1]"] through :input[name="checkboxes[option4]"] would get the state=disabled). I looked at misc/states.js and it is not doing anything even remotely like this suggestion yet, it's simply applying the states to the selectors in question. So this enhancement may not be fixable without API breakage (so therefore may only be fixable for Drupal 8).

If that's the case, then we need to at least update the documentation to make it clear you can't do this and why. If there is a simple workaround (perhaps some custom jQuery you could add with drupal_add_js) we could document that too. The documentation as it stands is a bit misleading in that it demonstrates enabled / disabled with single checkboxes (which works fine), and then demonstrates visible with groups of checkboxes (also works fine), without mentioning that enabled/disabled will not work in this case.

nod_’s picture

Version: 7.x-dev » 8.x-dev
Component: documentation » forms system
Status: Postponed (maintainer needs more info) » Needs work
Issue tags: +JavaScript

Great work, thanks :) that makes it come back on the table for D8.

I'm assiging that for FAPI, it may be a solution to do something in form_process_checkboxes().

I don't think it'd be too much trouble to make that work in JS. If it's a JS only solution the part that needs to change is the function: $(document).bind('state:disabled', function(e) { }); in states.js line 490.

Cyberwolf’s picture

Thanks guys for finally taking a decent look at the issue and confirming it!

damien_vancouver’s picture

It was bothering me that I thought this worked for Field API fields and not Form API fields, so I recreated the test fields in a content type and then tried adding the #states array via hook_form_alter. But it turns out it was only working for me before because I was using a type=select, and it only worked for #6 above because acbramley was using a single checkbox. It definitely doesn't work for Field API type=checkboxes or type=radios for the same reasons as my testing in #17.
So that clears up any remaining mystery in my mind.

@nod_, it sounds like in your expert opinion the changes aren't that drastic. Since Field API isn't working like I thought, if we were able to improve states.js so that it quietly just handled this properly as the documentation would lead you to expect, then would it count as an API change for the purposes of backporting it?

My jQuery is way too terrible to attempt to write this as a patch, but how about this logic added to states.js $(document).bind('state:disabled', function(e) { ?

 // ...
 // existing .bind('state:disabled') logic here
 // ...

 // If the element is a container full of checkboxes or radios, 
 // each descendent checkbox or radio element must also have its state set.
 if $(e.target) element type == "checkboxes" {
    apply .attr('disabled', e.value);   to all descendent elements of type=checkbox
 } elseif  $(e.target) element type == "radios" {
    apply .attr('disabled', e.value);  to all descendent elements of type=radio
}

Sorry about the lame pseudocode... but am I correct in believing that's a fairly trivial js change? and if so would it be eligible for backport?

emosbaugh’s picture

Status: Needs work » Needs review
FileSize
914 bytes

I think it can be handled in the preprocess functions of the radios and checkbox elements. Patch for drupal 8.x attached.

The issue with this patch is that the "disabled" attribute is still (as it is prior to this patch) applied to the outer div element (see markup below). This results in what I would think is invalid HTML markup. I would propose that with the #states element is unset to the original element in the process function or there is some filter in the js to not apply the attribute to the div element. This is a much larger change and I am hesitant to include it in this patch.

<div id="edit-radios" class="form-radios" disabled="disabled">
  <div class="form-item form-type-radio form-item-radios">
    <input type="radio" id="edit-radios-option1" name="radios" value="option1" class="form-radio" disabled="disabled">  <label class="option" for="edit-radios-option1">Option 1 </label>
  </div>
  <div class="form-item form-type-radio form-item-radios">
    <input type="radio" id="edit-radios-option2" name="radios" value="option2" class="form-radio" disabled="disabled">  <label class="option" for="edit-radios-option2">Option 2 </label>
  </div>
</div>
marcingy’s picture

Status: Needs review » Reviewed & tested by the community

This patch works nicely. I do however wonder if we need tests for this?

attiks’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs tests

Please add some tests so we can make sure this really works now and in the future.

jhodgdon’s picture

Status: Needs review » Needs work

If it needs tests, it's "needs work" status. :)

Alan D.’s picture

Maybe related, but I instantly bypassed this issue via an after_build without a second thought...

I'm trying to get the radios on the Diff module page to disable / hide if the corresponding one is set.

  $form['diff']['old'] = array(
    '#type' => 'radios',
    '#options' => $revision_ids,
    '#default_value' => $old_vid,
    '#after_build' => array('diff_form_process_revision_radios'),
  );
  $form['diff']['new'] = array(
    '#type' => 'radios',
    '#options' => $revision_ids,
    '#default_value' => $new_vid,
    '#after_build' => array('diff_form_process_revision_radios'),
  );

// using
function diff_form_process_revision_radios($element, $form_state) {
  $parent = current($element['#parents']) == 'new' ? 'old' : 'new';
  foreach (element_children($element) as $vid) {
    $element[$vid]['#states'][] = array(
      'disabled' => array(
       ':input[name="' . $parent . '"]' => array('value' => "$vid")
      )
    );
  }
  return $element;
}

Everything appears to be getting set, Drupal.settings {}, form names, ids, values, et al, but the only effect on the diff page is that the very first checkbox on the left at the top is disappearing after about 0.1 ms (about the time I would expect the 80 or so radios to take to be processed). So can radios trigger radios using states? I tried with visible, invisible, disabled.

Alan D.’s picture

Related to tests, are their any #state tests?

attiks’s picture

#26 only way to really test them is using javascript, we build testswarm and are currently testing the following for states (D7): http://drupalcode.org/project/testswarm.git/blob/refs/heads/7.x-1.x:/tes... (Live demo)

There's a Drupal 8 version as well but it's lagging a bit, but most tests will work for both D7 and D8.

pwaterz’s picture

I tried the patch above, and it ignores default values on the checkboxes element. It defaults them to be all unchecked. Here is the code that I used,

 $form['select_all'] = array(
      '#type' => 'checkbox',
      '#title' => t('Select All'),
    );

    $form['collections'] = array(
      '#type' => 'checkboxes',
      '#title' => isset($conf['checkbox_heading']) ? $conf['checkbox_heading'] : 'Alert me to articles on the following topics',
      '#options' => $options,
      '#default_value' => $default_value,
      '#states' => array(
          'checked' => array(
                ':input[name="select_all"]' => array('checked' => TRUE),
              ),
          ),
    );
jaykainthola’s picture

Hi,

When I am using "#states" property for a first checkbox to checked when another checkbox is checked. All is working fine, except that default value of first checkbox is being ignore.

Can any one help me out?

Thanks

DuaelFr’s picture

I have the same issue on a select field.
After investigating a bit it was fully evident that setting the checked/disabled attribute on a wrapper could never do the job.

The attached patch is working well for me with select, checkboxes and textfields but #21 was not changing anything for me.
As far I know there is no tests on JS files so I turn this issue back to Needs Review.

This patch is part of the #1day1patch initiative.

DuaelFr’s picture

forko’s picture

I was trying to check checkbox... 2 days...
#5 worked for like charm...

Garrett Albright’s picture

Issue summary: View changes
jhedstrom’s picture

Status: Needs review » Needs work
Issue tags: +Needs re-roll, +Needs manual testing

This needs a re-roll, if still applicable. The checkbox logic has changed to this in states.js:

 $(document).on('state:checked', function (e) {
    if (e.trigger) {
      $(e.target).prop('checked', e.value);
    }
  });
jhedstrom’s picture

Issue tags: -Needs re-roll +Needs reroll
DuaelFr’s picture

Reroll of #31
D7 patch is unchanged. D8 patch has been adapted.

berimbolo’s picture

I've had the same issue with radio buttons for Drupal 7.40 due to the event handler being attached to the radio button of interest rather than the entire set.

This seems to have fixed it (states.js @ approx line 350):

Replace:

// Attach the event callback.
    this.element.bind(event, $.proxy(function (e) {

with:

// Attach the event callback.
        var $el;
	if(this.element.length && (this.element[0].tagName.toUpperCase() == 'INPUT') && (this.element[0].type.toLowerCase() == 'radio'))
		$el = this.element.closest('form').add(document.body).last().find("input[type='radio'][name='" + this.element[0].name + "']");
	else
		$el = this.element;
    $el.bind(event, $.proxy(function (e) {
andypost’s picture

re-roll of issue and added module for manual testing:
1) enable it and visit /issue/994360
2) make sure checkboxes and radios got "disabled" property changed

timisoreana’s picture

FileSize
24.19 KB
18.14 KB
25.99 KB
17.57 KB

Tested on /issue/994360

  1. Checked enabled elements
    • Checkbox

      Enabled checkbox

    • Radio

      radio enabled

  2. Checked disabled elements
    • Checkbox

      Checkbox disabled

    • Radio

      radio disabled

Actual result=Expected result: Are disabled/enabled only checkboxes/radio buttons themselves, and not div around.
Test result: Passed

timisoreana’s picture

Status: Needs review » Reviewed & tested by the community
Wim Leers’s picture

Sadly we can't write tests for such JS-only things. Manual testing was done in #40. This needs to be backported to D7.

It'll be great to have this fixed :)

  • catch committed 65c7206 on 8.1.x
    Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot disable/...

  • catch committed 9c55478 on 8.0.x
    Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot disable/...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Yes #states is somewhere we really miss having js testing.

Committed/pushed to 8.1.x and cherry-picked to 8.0.x. Thanks!

esclapes’s picture

Status: Fixed » Needs work

I am afraid this patch brakes unchecking a checkbox based on radio value. This is my current #states condition setup:

$form['paper'] = array(
      '#type' => 'checkbox',
      '#states' => [
        'disabled' => [
          '.price-select input[type="radio"]' => array('value' => '48'),
        ],
        'unchecked' => [
          '.price-select input[type="radio"]' => array('value' => '48'),
        ],

      ],
      '#title' => $this->t('Paper version'),
    );

It works fine before the patch, but it does not uncheck with the patch applied

  • catch committed 8fd7d9b on 8.1.x
    Revert "Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot...

  • catch committed 898a110 on 8.0.x
    Revert "Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot...
catch’s picture

Reverted from both branches, thanks for the quick report.

esclapes’s picture

FileSize
1.85 KB

I might be missing something because in my current 8.0.2 install I cannot reproduce the original issue reported here. disabled attribute gets applied with and without the patch.

Anyway I have updated the issue_test.zip file with my use case.

droplet’s picture

FileSize
921 bytes

Check in.

I think this issue is looking for following patch's fixes.

I'd say this is an API change (behavior changes). Developers used it before, that won't expected Disabled is also Unchecked anything.

EDIT: this patch has buggy.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

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

jibran’s picture

jibran’s picture

Issue tags: +JavaScriptTest
DuaelFr’s picture

@droplet I think that any disabled field is sent by the browser on submit. This is how HTML forms are workingworking by design. So, if we want to avoid disabled values to be considered as empty, we need to repopulate them server side, before saving data.

We may use a hidden field to track disabled fields. This field could be prepopulated by the server when building the form then updated with JavaScript on submit. If the browser doesn't have JS enabled the disabled state of the field shouldn't be changed so the initial prepopulated list of disabled fields should remain reliable.

This kind of feature could be a great improvement for DX but it's out of the scope of this issue. Same thing applies to the validation of the fields which required status depend on states that might be automated.

maria sony’s picture

I created my custom module(contact form) with theming but radios doesn't appear in form Drupal 8
<?php
/**
* Created by PhpStorm.
* User: sonia
* Date: 28/02/2016
* Time: 23:23
*/

/**
* @file
* Contains \Drupal\contact_form\Form\ContributeForm.
*/

namespace Drupal\contact_form\Form;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ChangedCommand;
use Drupal\Core\Ajax\CssCommand;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\InvokeCommand;

/**
* Contribute form.
*/

class ContributeForm extends FormBase implements FormInterface{

/**
* {@inheritdoc}
*/
public function getFormId() {
return 'contact_form_contribute_form';
}

/**
* {@inheritdoc}
*/

public function buildForm(array $form, FormStateInterface $form_state) {

$form['FullName'] = array(
'#type' => 'textfield',
'#title' => $this->t('Full Name'),
'#required' => TRUE,

);
$form['Email'] = array(
'#type' => 'email',
'#title' => $this->t('Email address'),
'#required' => true,
'#size' => 60,
'#maxlength' => 128,

);

$form['Feedback'] = array(
'#type' => 'radios',
'#title' => $this->t('Feedback'),
'#description' => t('Type of feedback'),
'#default_value' => 0,
'#options' => array(
0 =>'Comments or suggestions',
1 => 'Questions',
2 =>'Report a problem(s)',
3 =>'Other',
)
);
$form['Subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#required' => true,
);
$form['Message'] = array(
'#type' => 'textarea',
'#title' => $this->t('Your Message'),
'#required' => true,
'#cols' => 90,
'#rows' => 10,
);

$form['captcha'] = array(
'#type' => 'captcha',
'#captcha_type' => 'recaptcha/reCAPTCHA',
'#title' => $this->t('Captcha:'),
'#required' => true,
);

$form['save'] = array(
'#type' => 'submit',
'#input' => TRUE,
'#value' => 'save',
'#ajax' => array(
'callback' => 'demo_pane_ajax_insert_row',
'method' => 'replace',
'effect' => 'fade',
),
);

return array(
'#form' => $form,
'#theme' => 'contribute',

);

}

/**
* {@inheritdoc}
*/

public function validateForm(array &$form, FormStateInterface $form_state) {
}

/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {

}

function demo_pane_ajax_insert_row(array $form, FormStateInterface &$form_state) {

$name = $form_state->getValue('FullName');
$email = $form_state->getValue('Email');
$subject = $form_state->getValue('Subject');
$type = $form_state->getValue('TypeFeedback');
$msg = $form_state->getValue('Message');
$field = array(

'FullName' => $name,
'Email' => $email,
'TypeFeedback' => $type ,
'Subject' => $subject,
'Message' => $msg,
'Etat_message'=> '',
'Date_contact'=> '',
'adresse_IP'=> '',
);
db_insert('contact')
->fields($field)
->execute();
drupal_set_message("succesfully saved");

}
}

DuaelFr’s picture

Hi @maria sony,
First, please use the code or php button on the toolbar to make your code readable.
Then, this issue is about the #states option in forms but I can't see any in your code. I suppose that you need some support but you're not in the right place, so I'd suggest that you go to the support page to find out where to post your message.
I hope you'll find the help you need.


I added the "Needs issue summary update" tag because this issue has moved a lot and I'm not sure maintainers would commit it even with a fully working and tested patch.

Triss82’s picture

Thanks DuaelFr, this patched worked fine for me.

Yet, another patch that need apply on all my Drupal sites :(.

  • catch committed 65c7206 on 8.3.x
    Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot disable/...
  • catch committed 8fd7d9b on 8.3.x
    Revert "Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot...

  • catch committed 65c7206 on 8.3.x
    Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot disable/...
  • catch committed 8fd7d9b on 8.3.x
    Revert "Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot...

Version: 8.1.x-dev » 8.2.x-dev

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.

  • catch committed 65c7206 on 8.4.x
    Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot disable/...
  • catch committed 8fd7d9b on 8.4.x
    Revert "Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot...

  • catch committed 65c7206 on 8.4.x
    Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot disable/...
  • catch committed 8fd7d9b on 8.4.x
    Revert "Issue #994360 by DuaelFr, andypost, emosbaugh: #states cannot...

Version: 8.2.x-dev » 8.3.x-dev

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.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should 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.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should 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.

gapple’s picture

This issue references issues with 'disabled', but all of the patches deal with 'checked'.

From what I can tell, the issue with disabled was fixed by #1263302: States API disabled state handler filters out nonexistent class "form-element" and doesn't disable certain form element types, which was committed in October 2012.

gapple’s picture

Title: #states cannot disable/enable radios and checkboxes » #states cannot check/uncheck 'radios' and 'checkboxes' elements
Version: 8.5.x-dev » 8.6.x-dev
Issue summary: View changes
Related issues: +#1263302: States API disabled state handler filters out nonexistent class "form-element" and doesn't disable certain form element types

Updating Issue title and summary to reflect still present issue with 'checked'

gapple’s picture

Here's a patch that applies the same strategy that was always done for setting 'disabled': change the attribute on all children of the closest form item or wrapper.

jQuery .closest() will return either the element itself if it matches the requested selector or the nearest parent that matches, so the two calls to .prop() that were used for setting disabled should not be needed.

gapple’s picture

Forgot a small change in the last patch.

'disabled' needs to check for all form types, 'checked' only needs to look for radios and checkboxes, so the selectors can be narrowed a bit.

gapple’s picture

Found the bigger issue.

template_preprocess() copies all of the attributes from the form element's #attributes properties to the template $variables['attributes']. However, in template_preprocess_checkboxes() and template_preprocess_radios(), $variables['attributes'] is re-initialized to an empty array removing the data-drupal-states and data-drupal-selector properties.

gapple’s picture

Opened the related issue #2945727: Form radios/checkboxes elements should have js-form-wrapper class, which can be addressed independently of this issue, and will be a much bigger problem once this issue is resolved.

Status: Needs review » Needs work

The last submitted patch, 71: drupal-994360-71-states-checkboxes-checked.patch, failed testing. View results

gapple’s picture

The last patch failed because the disabled attribute was now getting set on the form-checkboxes wrapper, as well as the individual input elements.

This patch unsets the disabled attribute in template_preprocess_radios() / template_preprocess_checkboxes()

Version: 8.6.x-dev » 8.7.x-dev

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.

Anybody’s picture

The last patch looks very good. How can we proceed here? Perhaps it would make sense to add JS tests to ensure states will behave as expected in the future and code the examples here which lead to problems?

Version: 8.7.x-dev » 8.8.x-dev

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.

texas-bronius’s picture

Patch in #74 visually works for me for my use case: Needs to toggle checked states of other checkboxes on a form based on one a remote checkbox:

      foreach (['twitter', 'facebook', 'instagram'] as $url_field) {
        $form['salesforce_override_' . $url_field]['#states'] = [
          'checked' => [
            'input[name="salesforce_override_all[value]"]' => ['checked' => TRUE],
          ],
        ];

Before, no effect, with patch it works: All checkboxes toggle on and off in unison with the "parent" remote checkbox.

But is this the right place to ask about whether the #states `checked` should also be detected by the following rule (not shown here) that when the child checkboxes are checked, the input field they respect should be disabled? This is working on individual checkbox toggle, but when I used the parent remote (which this patch allows to even work), the child checkboxes' toggles are not detected / acted upon by their respective text field #states.

Version: 8.8.x-dev » 8.9.x-dev

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.

Version: 8.9.x-dev » 9.1.x-dev

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.

Version: 9.1.x-dev » 9.2.x-dev

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.

JordiK’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -JavaScript +JavaScript
acbramley’s picture

Status: Reviewed & tested by the community » Needs work

Patch needs a reroll and tests.

dww’s picture

Happy to report that #2702233: [backport] Add JavaScript tests for Form API #states: required, visible, invisible, expanded, checked, unchecked is now in the 9.2.x branch (and hopefully backported to the other branches soon), which will be a great place to add the tests for this issue.

Let me know if anyone has any questions on how those tests work.

Cheers,
-Derek

vsujeetkumar’s picture

Reroll the patch for 9.2.x.

vsujeetkumar’s picture

Status: Needs work » Needs review

Version: 9.2.x-dev » 9.3.x-dev

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.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

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.

Version: 9.5.x-dev » 10.1.x-dev

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.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
FileSize
143 bytes

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.

reszli’s picture

re-roll for 9.5.x

Abhisheksingh27’s picture

Status: Needs work » Needs review
FileSize
2.08 KB

Adding Reroll for D10

dww’s picture

Status: Needs review » Needs work

This still needs (as tagged):

Sadly, patch #93 lost the changes to core/misc/states.es6.js and is invalid. We need to go back to at least #92.

Back to Needs work.

longwave’s picture

Issue tags: -Need tests +Needs tests

Fixing tag

dww’s picture

Title: #states cannot check/uncheck 'radios' and 'checkboxes' elements » #states cannot check/uncheck checkboxes elements
Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs tests, -Needs issue summary update
FileSize
13.1 KB
13.84 KB
13.91 KB

It took me a little while to write a failing test here. 😅 If you set #states on the specific values of a checkboxes element, it works fine, even without the patch. E.g. this works fine, before/after the patch:

    $form['checkboxes_some_checked_when_checkbox_trigger_checked'] = [
      '#type' => 'checkboxes',
      '#title' => 'Checkboxes: some checked when checkbox trigger checked',
      '#options' => [
        'value1' => 'Value 1',
        'value2' => 'Value 2',
        'value3' => 'Value 3',
      ],
      'value1' => [
        '#states' => [
          'checked' => [
            ':input[name="checkbox_trigger"]' => ['checked' => TRUE],
          ],
        ],
      ],
    ];

The only trouble is if you're trying to bulk check/uncheck all values of a checkboxes by setting #states on the parent layer of the form array, e.g. this:

    $form['checkboxes_all_checked_when_checkbox_trigger_checked'] = [
      '#type' => 'checkboxes',
      '#title' => 'Checkboxes: all checked when checkbox trigger checked',
      '#options' => [
        'value1' => 'Value 1',
        'value2' => 'Value 2',
        'value3' => 'Value 3',
      ],
      '#states' => [
        'checked' => [
          ':input[name="checkbox_trigger"]' => ['checked' => TRUE],
        ],
      ],
    ];

From what JavascriptStatesTest can tell us, type radios works fine, both for selecting and enable/disable.

I have no idea what people expect to happen if you tell a whole set of radio buttons to be "checked" when another trigger is hit. This would make no sense:

    $form['radios_checked_when_checkbox_trigger_checked'] = [
      '#type' => 'radios',
      '#title' => 'Radios checked when checkbox trigger checked',
      '#options' => [
        'value1' => 'Value 1',
        'value2' => 'Value 2',
      ],
      '#states' => [
        // Which value is supposed to be checked now? WTF does this code expect? 😂
        'checked' => [
          ':input[name="checkbox_trigger"]' => ['checked' => TRUE],
        ],
      ],
    ];

I believe you should have to say this, which works fine:

    $form['radios_checked_when_checkbox_trigger_checked'] = [
      '#type' => 'radios',
      '#title' => 'Radios checked when checkbox trigger checked',
      '#options' => [
        'value1' => 'Value 1',
        'value2' => 'Value 2',
      ],
      'value1' => [
        '#states' => [
          'checked' => [
            ':input[name="checkbox_trigger"]' => ['checked' => TRUE],
          ],
        ],
      ],
    ];

Meanwhile, all of the variations of trying to enable/disable either specific checkboxes or radios, or the entire element, seems to work fine without the patch. I don't think the hunk in form.inc is needed. Updating the title and summary to clarify this is only really about fixing the bulk check/uncheck on checkboxes.

However, given all the confusion in this issue, and since I already spent the time to add all the different permutations of the test coverage, I think it's worth expanding the scope a bit here to ensure we're covering all these cases, even if only 1 of them is actually broken in core and solved by the rest of the patch. I hope the committers agree we don't need a follow-up to add the additional coverage of the currently-working permutations.

dww’s picture

Some self-review to document what I've done and why:

  1. +++ b/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
    @@ -180,6 +180,123 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +    $form['checkboxes_all_checked_when_checkbox_trigger_checked'] = [
    +      '#type' => 'checkboxes',
    +      '#title' => 'Checkboxes: all checked when checkbox trigger checked',
    +      '#options' => [
    +        'value1' => 'Value 1',
    +        'value2' => 'Value 2',
    +        'value3' => 'Value 3',
    +      ],
    +      '#states' => [
    +        'checked' => [
    +          ':input[name="checkbox_trigger"]' => ['checked' => TRUE],
    +        ],
    +      ],
    +    ];
    

    This is the only new form element in this test that fails without the fix to states.js.

  2. +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php
    @@ -8,6 +8,11 @@
    + * The form being tested is JavascriptStatesForm provided by the 'form_test'
    + * module under 'system' (core/modules/system/tests/module/form_test).
    + *
    + * @see Drupal\form_test\Form\JavascriptStatesForm
    + *
    

    This seems like really helpful documentation to enable other people to expand this test coverage, so I'm not the only one doing it in the future. 😅 Please don't object to the "scope creep". 🙏

  3. +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php
    @@ -94,6 +99,53 @@ protected function doCheckboxTriggerTests() {
    +    $radios_all_disabled_value1 = $this->xpath('//input[@name=:name][@value=:value]', [':name' => 'radios_all_disabled_when_checkbox_trigger_checked', ':value' => 'value1']);
    

    I couldn't figure out a way to get a specific radio button inside a set of radios using only findField(). If someone knows of a more slick / concise way to write this, please let me know.

  4. +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php
    @@ -117,6 +187,30 @@ protected function doCheckboxTriggerTests() {
    +    $this->assertTrue($checkboxes_all_checked_element_value1->isChecked());
    +    $this->assertTrue($checkboxes_all_checked_element_value2->isChecked());
    +    $this->assertTrue($checkboxes_all_checked_element_value3->isChecked());
    

    Without the fix to states.js these are the only 3 new assertions that fail. If you comment these out, and revert the fix, everything else still passes. But given there's almost no cost to running the next 20 lines worth of assertions (no new page load needed, etc), I strongly vote we keep the rest of this.

Thanks!

The last submitted patch, 96: 994360-96.test-only.patch, failed testing. View results

longwave’s picture

Status: Needs review » Reviewed & tested by the community

Beautiful work. The fixes look simple - in both the disabled and checked cases, we only want to set the property on the input itself and not on any parent wrapper; this brings the code that handles the two cases more inline with each other. The test coverage is also comprehensive, nice to get some additional test coverage on this functionality.

#97.2 I agree that this additional documentation is helpful and is fine to add in this patch.

#97.3 As far as I can see the only way here would be to use unique labels for each radio button, but that would make the form more complicated, so I think using XPath here is fine.

While reviewing I also noted this comment in states.js:

      // Note: WebKit nightlies don't reflect that change correctly.
      // See https://bugs.webkit.org/show_bug.cgi?id=23789

This bug was fixed in 2011! Opened #3347492: Remove outdated comment about WebKit from states.js to remove this.

lauriii’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
16.02 KB
2.84 KB

This bug got almost fixed 7 years ago but got reverted because of a regression 🤯 I double checked that the regression doesn't occur anymore, but looks like we don't have test coverage for that use case. Here's a new patch that adds test coverage for that.

longwave’s picture

Status: Needs review » Reviewed & tested by the community

The addition in #100 makes sense to me. I thought about extracting the initial state test to its own method so we don't have to copy-paste and keep the two parts in sync, but it would mean having way too many arguments for each of the fields that are tested, so I think this is OK as-is.

  • lauriii committed fdb1fa9f on 10.1.x
    Issue #994360 by DuaelFr, gapple, dww, andypost, lauriii, emosbaugh,...
lauriii’s picture

Status: Reviewed & tested by the community » Fixed

Committed fdb1fa9 and pushed to 10.1.x. Thanks!

Anybody’s picture

Wohooooooooooooo!! Thank you all so much! Fixed after 13 years!! This makes me so happy to see.
Honestly, thank you!

DuaelFr’s picture

O.M.G!
Thank you all for having the energy to push this to the end!

dww’s picture

Version: 10.1.x-dev » 9.5.x-dev
Status: Fixed » Needs review
FileSize
17.04 KB
2.28 KB

Re: #100: Thanks for the info and updated test coverage!

Re: #101: Yeah, I had the same inital "yuck" thought, but indeed, it'd be clumsy to pass all those elements to a private helper, so this is probably fine.

I asked in Slack, and @longwave and @lauriii agreed this was eligible for backport. The commit should cherry-pick cleanly to 10.0.x already, but in the 9.5.x branch, we're still using states.es6.js. So here's an updated patch.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Excited to see this make it!

dww’s picture

Issue summary: View changes

Thanks! Updated the summary to include the current remaining tasks (to make it easier for whichever committer sees this in the RTBC queue)...

  • longwave committed 0b49a11c on 10.0.x authored by lauriii
    Issue #994360 by DuaelFr, gapple, dww, andypost, lauriii, emosbaugh,...

  • longwave committed 1c584148 on 9.5.x
    Issue #994360 by DuaelFr, gapple, dww, lauriii, andypost, emosbaugh,...
longwave’s picture

Status: Reviewed & tested by the community » Fixed

@dww Thanks for the clear instructions.

Cherry-picked fdb1fa9ff0 to 10.0.x and committed #106 to 9.5.x. Thanks everyone!

dww’s picture

Yay, thanks!

nevergone’s picture

If possible please help this issue: #3267246: ['#states']['required'] broken for radios
Thanks!

Status: Fixed » Closed (fixed)

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

alex.skrypnyk’s picture

Changing
$(e.target).prop('disabled', e.value).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value).find('select, input, textarea').prop('disabled', e.value);

to

$(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value).find('select, input, textarea').prop('disabled', e.value);

has broken disabling items if there is no `.js-form-wrapper` around an element.

Previously, the code worked because the element itself was targeted first. Now, the element is found and then it's children are searched for select, input, textarea. It would worked if the found element was .js-form-wrapper. But there could be cases when .js-form-wrapper could be missing.

Not sure why we are targeting different levels of selectors here - the form item and form wrapper are on different levels in the hierarchy and should not be treated in the same way (same selector query).

And this also limited 'disabled' to select, input, textarea elements, but previously it would work for any elements, including button

david.muffley’s picture

Like #115 I'm seeing an issue with the same change. We have a button element inside a form and it has the states settings and the js-form-submit class. Before its disabled status was being controlled due to that first `.prop('disabled', e.value)`, and now it's not working.

Probably needs to have a new issue for that, right?

mt-i-1’s picture

Find a small workaround, you need to change the HTML structure of your button/input submit (done on 9.5.7) .

1) Remove the .js-form-submit (example here with twig)

<input{{ attributes.removeClass('js-form-submit') }} />{{ children }}

2) Add .js-form-item around your input

    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => t('Validate'),
      '#enabled' => FALSE,
      '#prefix' => '<div class="js-form-item">',
      '#suffix' => '</div>',
    ];

    $form['actions']['submit']['#states'] = [
      'enabled' => [
        [
          ':input[name="other_input"]' => ['empty' => FALSE],
        ],
      ],
    ];


david.muffley’s picture

Thanks mt-i-1. I'll go with a parent container element which has the same effect. And I'll stop spamming this closed issue.

gapple’s picture

I've opened #3354998: #states disable property has stopped working for submit button and other elements as a followup with a MR to update the element types that get the disabled property set.