5

In my Content type, I add a field Reference: User with unlimited values, Reference method from a view of user managers, and widget checkboxes.

Basically when a user manager edit the node, he can only check the box with his own name. The other checkboxes are set to disabled. The node will be published if all managers check the box.

I've tried creating form_alter function like below

function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {
    if ($form_id === 'node_request_order_edit_form') {

        $field_approved_by = $form['field_approved_by']['widget']['#options'];
        
        foreach($field_approved_by as $key=>$val) {
            if($key != \Drupal::currentUser()->id()) {
                $form['field_approved_by']['widget']['#options'][$key]['#disabled'] = true;
            }
        }
    }
}

But I got error

Error: Cannot use object of type Drupal\Core\Field\FieldFilteredMarkup as array...

If I remove the checkboxes using unset,

    ...
    if($key != \Drupal::currentUser()->id()) {
        unset($form['field_approved_by']['widget']['#options'][$key]);
    }
    ...

The value get overwritten when different manager checks the box and save the node.

Thanks for any help.

UPDATE 1:

So, I kind of solve it by using JavaScript to disable these checkboxes. Here are the reference tutorials:

But I don't know whether the performance/stability is better or worse than the provided solution from the answers.

Basically I create a JS library file: spo_core.libraries.yml

request_order_edit:
  js:
    js/request_order_edit.js: {}
  dependencies:
    - core/jquery
    - core/jquery.once
    - core/drupal
    - core/drupalSettings

Then in JS folder, create file: request_order_edit.js

(function ($) {
  'use strict';
  Drupal.behaviors.addLayer = {
    attach: function (context, settings) {
        console.log(drupalSettings.spo_core.current_uid);
        $('#edit-field-approved-by input:checkbox').each(function(){
            if(this.value != drupalSettings.spo_core.current_uid){
                this.disabled = true;
            }
        });

        /*enable the checkbox before submitting so the disabled checkbox can post value*/
        $("form#node-request-order-edit-form").submit(function() {
        $('#edit-field-approved-by.form-checkboxes input:checkbox').removeAttr("disabled");
    });
    }
  }
})(jQuery);

Then attach it in form alter like this

...
$form['#attached']['library'][] = 'spo_core/request_order_edit';
$form['#attached']['drupalSettings']['spo_core']['current_uid'] = \Drupal::currentUser()->id();
...
3
  • This could be a good opportunity to create a custom field widget that extends core's checkboxes widget rather than using a form alter hook.
    – sonfd
    Aug 24, 2021 at 20:32
  • @sonfd Thanks for the answer. I've updated my question adding my workaround using JavaScript. But I But I don't know whether the performance/stability/standardization compared with your solution/answer. what do you think?
    – Bonn
    Aug 25, 2021 at 1:59
  • When I had a similar scenario (disabling some checkboxes in a field's element), I created a Field Widget that extended core's and used the Form Options Attributes module to disable the checkboxes. In my opinion, that's the best solution. With your javascript approach, it's possible, though unlikely, someone could check a box before it gets disabled.
    – sonfd
    Aug 25, 2021 at 11:55

2 Answers 2

4

As far as I know, Drupal core does not provide this functionality out of the box - there's no way to add attributes to an element's individual options.

However, one way I've done this is with the help of the Form Options Attributes module.

[The Form Options Attributes] module adds the ability to specify attributes for individual options on Drupal Form API elements of the types: select, checkboxes, and radios.

It's pretty easy to use. From the project page:

To add attributes to a form element's options, add an #options_attributes key to the form element definition. The #options_attributes value should be an array with keys that match the keys in the #options value array. The values in the #options_attributes array should be formatted like the main #attributes array.

For example:

$form['my_field'] = [
  '#options' => [
    '1' => 'User 1',
    '123' => 'User 123',
    '456' => 'User 456',
  ]
  '#options_attributes' => [
    // Disable this option.
    '1' => [
      'disabled' => 'disabled',
    ],
    // Allow this option.
    '123' => [],
    // Disable this option.
    '456' => [
      'disabled' => 'disabled',
    ],
  ],
];

Or see the module's README for more complete examples.

0
2

Do this:

function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {
    if ($form_id === 'node_request_order_edit_form') {

        $field_approved_by = $form['field_approved_by']['widget']['#options'];
        
        foreach($field_approved_by as $key=>$val) {
            if($key != \Drupal::currentUser()->id()) {
                $form['field_approved_by']['widget'][$key]['#disabled'] = true;
            }
        }
    }
}

The key is that when you specify the attributes of the options, you work with the child element of 'widget', not of '#options'.

1
  • 1
    Thanks. This did the trick for disabling or hiding Group Roles for Members of a Group. $form['group_roles']['widget']['role-name']['#disabled'] = true; or $form['group_roles']['widget']['role-name']['#access'] = false; Apr 14, 2022 at 16:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.