Drupal 8 How to set the default format in text_format and hide the select programmatically

Sometimes you need to set a default format for text_format (to Full HTML for example) and hide the select list based on some condition, for instance you don't want users with editor role to change the text format of your field (The marked area below).

Image
Text format screenshot

The first thing you think about when you heard alter a field programmatically is the hook_form_alter  but it's not enough in this case let's take a look on the following code snippet:

/**
 * Implements hook_form_alter().
 */
function MY_MODULE_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'node_article_edit_form') {
    // See the result of this dump in screen shoot.
    dpm($form['field_text_content']);
  }
}

The dump of your field using devel module looks like:

Field dump using devel

So now as you can see we don't have access to format to alter it!

The solution is to use hook_field_widget_form_alter to get access to the element array, even though it's not enough to alter the element we should add an #after_build callback like the following:

So let's implement our example:

For users with editor role should have only full HTML format, and they shouldn’t have access to select list to change it for our field_text_content field.

use Drupal\Core\Form\FormStateInterface;

/**
 * Implements hook_field_widget_form_alter().
 */
function MY_MODULE_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) { 
  // Get the current user.
  $current_user = \Drupal::currentUser();
  $user_roles = $current_user->getRoles();
  /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
  $field_definition = $context['items']->getFieldDefinition();
  // Check the field with the field name.
  // Check if the current user has editor role.
  // You can add your other conditions.
  if ($field_definition->getName() === 'field_text_content' && in_array('editor', $user_roles)) { 
    // Set only full_html as allowed format.
    $element['#allowed_formats'] = ['full_html'];
    // Add after build to remove the help wrapper and text.
    $element['#after_build'][] = '_field_text_content_after_build';
  }
}

Now let's implement our after_build callback.

/**
 * After build callback for field_text_content.
 * 
 * @param array $element
 *   Array element.
 * @param $form_state
 *   Form state.
 *
 * @return array
 *   New element.
 */
function _field_text_content_after_build(array $element, FormStateInterface $form_state) {
  if (isset($element['format'])) {
    // Remove Guidelines and Help text.
    unset($element['format']['help']);
    unset($element['format']['guidelines']);
    unset($element['format']['#type']);
    unset($element['format']['#theme_wrappers']);
  }
  return $element;
}

and we are done ✅ 

after clear cache you get something like!

Field after remove text format