Score:5

How to make a checkbox read-only/disabled from list of user entity references

cn flag

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();
...
sonfd avatar
in flag
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.
cn flag
@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?
sonfd avatar
in flag
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.
Score:4
in flag

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.

Score:2
cn flag

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'.

us flag
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;
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.