2

I want to modify the $form variable in an implementation of hook_form_FORM_ID_alter but can't seem to be able to set the #prefix and #suffix variables on field widget.

/**
 * @file
 * Implementations Module Hooks
 */

use Drupal\Core\Form\FormStateInterface;

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alter the node/create form for implementation types
 */
function implementations_form_node_implementation_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $prefix = '<div style="display:none;">';
  $suffix = '</div>';

  $form['field_parent_idea']['widget']['#prefix'] = $prefix;
  $form['field_parent_idea']['widget']['#suffix'] = $suffix;

  dpm($form['field_parent_idea']['widget']);
  dpm($form['field_parent_idea']['widget']['#prefix']);
  dpm($form['field_parent_idea']['widget']['#suffix']);
}

When I navigate to node/add/implementation I would expect to see the value of $form['field_parent_idea']['widget'] with the #prefix and #suffix variables set, but instead they're blank. Here's the relevant snippet showing the suffix/prefix values:

[#max_delta] => 0
[#prefix] => 
[#suffix] => 
[add_more] => Array

Why are they blank?

This is the full output of what I see.


Array
(
    [0] => Array
        (
            [target_id] => Array
                (
                    [#title] => stdClass Object
                        (
                            [__CLASS__] => Drupal\Core\StringTranslation\TranslatableMarkup
                            [translatedMarkup:protected] => 
                            [options:protected] => Array
                                (
                                )

                            [stringTranslation:protected] => stdClass Object
                                (
                                    [__CLASS__] => Drupal\Core\StringTranslation\TranslationManager
                                    [translators:protected] => Array
                                        (
                                            [30] => Array(1)
                                        )

                                    [sortedTranslators:protected] => Array
                                        (
                                            [0] => Drupal\Core\StringTranslation\Translator\CustomStrings
                                        )

                                    [defaultLangcode:protected] => en
                                    [_serviceId] => string_translation
                                )

                            [string:protected] => @title (value @number)
                            [arguments:protected] => Array
                                (
                                    [@title] => Parent Idea
                                    [@number] => 1
                                )

                        )

                    [#title_display] => invisible
                    [#description] => 
                    [#field_parents] => Array
                        (
                        )

                    [#required] => 1
                    [#delta] => 0
                    [#weight] => 0
                    [#type] => entity_autocomplete
                    [#target_type] => node
                    [#selection_handler] => default:node
                    [#selection_settings] => Array
                        (
                            [target_bundles] => Array
                                (
                                    [idea] => idea
                                )

                            [sort] => Array
                                (
                                    [field] => _none
                                )

                            [auto_create] => 
                            [auto_create_bundle] => 
                            [match_operator] => CONTAINS
                            [match_limit] => 10
                        )

                    [#validate_reference] => 
                    [#maxlength] => 1024
                    [#default_value] => 
                    [#size] => 60
                    [#placeholder] => 
                )

            [_weight] => Array
                (
                    [#type] => weight
                    [#title] => stdClass Object
                        (
                            [__CLASS__] => Drupal\Core\StringTranslation\TranslatableMarkup
                            [translatedMarkup:protected] => 
                            [options:protected] => Array
                                (
                                )

                            [stringTranslation:protected] => stdClass Object
                                (
                                    [__CLASS__] => Drupal\Core\StringTranslation\TranslationManager
                                    [translators:protected] => Array
                                        (
                                            [30] => Array(1)
                                        )

                                    [sortedTranslators:protected] => Array
                                        (
                                            [0] => Drupal\Core\StringTranslation\Translator\CustomStrings
                                        )

                                    [defaultLangcode:protected] => en
                                    [_serviceId] => string_translation
                                )

                            [string:protected] => Weight for row @number
                            [arguments:protected] => Array
                                (
                                    [@number] => 1
                                )

                        )

                    [#title_display] => invisible
                    [#delta] => 0
                    [#default_value] => 0
                    [#weight] => 100
                )

        )

    [#theme] => field_multiple_value_form
    [#field_name] => field_parent_idea
    [#cardinality] => -1
    [#cardinality_multiple] => 1
    [#required] => 1
    [#title] => Parent Idea
    [#description] => 
    [#max_delta] => 0
    [#prefix] => 

    [#suffix] => 

    [add_more] => Array
        (
            [#type] => submit
            [#name] => field_parent_idea_add_more
            [#value] => stdClass Object
                (
                    [__CLASS__] => Drupal\Core\StringTranslation\TranslatableMarkup
                    [translatedMarkup:protected] => 
                    [options:protected] => Array
                        (
                        )

                    [stringTranslation:protected] => 
                    [string:protected] => Add another item
                    [arguments:protected] => Array
                        (
                        )

                )

            [#attributes] => Array
                (
                    [class] => Array
                        (
                            [0] => field-add-more-submit
                        )

                )

            [#limit_validation_errors] => Array
                (
                    [0] => Array
                        (
                            [0] => field_parent_idea
                        )

                )

            [#submit] => Array
                (
                    [0] => Array
                        (
                            [0] => Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget
                            [1] => addMoreSubmit
                        )

                )

            [#ajax] => Array
                (
                    [callback] => Array
                        (
                            [0] => Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget
                            [1] => addMoreAjax
                        )

                    [wrapper] => field-parent-idea-add-more-wrapper
                    [effect] => fade
                )

        )

    [#after_build] => Array
        (
            [0] => Array
                (
                    [0] => Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget
                    [1] => afterBuild
                )

        )

    [#field_parents] => Array
        (
        )

    [#parents] => Array
        (
            [0] => field_parent_idea
        )

    [#tree] => 1
)

At first I thought that the issue was because I was setting on a nested array variable, but that's exactly what the example code in the documentation does.


function hook_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if (isset($form['type']) && $form['type']['#value'] . '_node_settings' == $form_id) {
    $upload_enabled_types = \Drupal::config('mymodule.settings')
      ->get('upload_enabled_types');
    $form['workflow']['upload_' . $form['type']['#value']] = array(
      '#type' => 'radios',
      '#title' => t('Attachments'),
      '#default_value' => in_array($form['type']['#value'], $upload_enabled_types) ? 1 : 0,
      '#options' => array(
        t('Disabled'),
        t('Enabled'),
      ),
    );

    // Add a custom submit handler to save the array of types back to the config file.
    $form['actions']['submit']['#submit'][] = 'mymodule_upload_enabled_types_submit';
  }
}

2 Answers 2

3

For some reason dpm() fails to show #prefix & #suffix values, bug maybe? Use kint() instead. You'll need to enable the Kint module if you're using Devel 8.x-2.1. But if using Devel 4.0.1 you'll need to install kint via composer require kint-php/kint.

Dump aside, Drupal automatically strips the style= for security reasons.

Instead, it should be

$prefix = '<div class="visually-hidden">';

Note: Assuming you're using the default Seven theme as admin theme. Otherwise, you would have to add this class to your admin's theme CSS and hide it.


The more Drupally correct way is to use hidden

$form['field_parent_idea']['widget']['#type'] = 'hidden';

Another way is to set #access to false, but you won't be able to change the value via JavaScript as the field doesn't get printed to the page.

$form['field_parent_idea']['widget']['#access'] = FALSE;
6
  • Note If all of the above still fails, then you need to increase your modules weight as there is another custom or contrib mudule altering the same field; thus, you'll want your module's alter hook to execute after/last.
    – No Sssweat
    Oct 11, 2020 at 12:22
  • 1
    Drupal isn't the issue here. The poster is setting a PHP array value like this: $form['field_parent_idea']['widget']['#prefix'] = $prefix; immediately followed by dpm($form['field_parent_idea']['widget']['#prefix']);. Drupal doesn't process that value at all, there's no sanitation involved and the Form API doesn't come into play. This is purely PHP. And of course it should display the value. The problem here is that the expectation is wrong - if #prefix is a <div> tag, what should you expect to see when that value is rendered on an HTML page? Nothing. Which is exactly what happens.
    – anonymous
    Oct 11, 2020 at 20:33
  • I'm going to mark this as the answer as it helped me realise both why the original div changes weren't working, but also pointed me in the right direction regarding hiding the element at the hook level. Note that the solution ended up being $form['field_parent_idea']['#type'] = 'hidden'; rather than modifying the widget element, but that might be a field-specific thing.
    – quant
    Oct 11, 2020 at 22:05
  • Yes, this answer is relevant to what I called the second problem "the form is not visibly altered as you expect". If you get to the point where you are modifying the correct form and adding the #prefix, then 'style' is not something that will work. But as to what I called the first problem, this is not a bug with dpm().
    – anonymous
    Oct 11, 2020 at 22:11
  • 3
    I'll accept 'downside', but this is how it's *always been when using dpm() or even just var_export() in drupal_set_message(). I tested it too, before I posted: dpm(['#prefix' => '<div>']); will display just Array ( [#prefix] => ) in the message area, but if you look at the page source you will see the "<div>" like I said. This is how HTML works - if you try to print an HTML tag, the browser interprets it as HTML. (*always meaning since at least Drupal 5 ...)
    – anonymous
    Oct 11, 2020 at 22:23
3

There are two basic problems here. First, your debug output doesn't display what you expect, and second the form is not visibly altered as you expect.

  1. dpm() displays the output in the messages area of the page. What you are trying to display is an array containing an HTML tag as one of its values - that value will be interpreted as an HTML tag when shown in the messages area. <div> produces no visible output, which is why you think those array elements are "blank". But if you look at the page source you will see the <div>. Try using a regular non-HTML string as #prefix and #suffix and you will see this string in the output from the dpm(). First problem solved.

  2. You're altering $form['field_parent_idea']['widget'] but you don't show us what that form element is. Not all form elements respect #prefix and #suffix, so without knowing the element we don't know what to expect. Indeed, your verbose output looks like you're using a custom #theme function, so it's even less clear what we expect to see. It's highly likely that your theme function is ignoring what you put into #prefix and #suffix.

When doing something like this for the first time, it's best if you start off with some simple things and get those working first. Have you successfully used hook_form_FORM_ID_alter() elsewhere? Try just changing the form title, for example, and see if you can get that to work. Try adding a #prefix to the "Title" textfield and get that to work. Taking these little steps and getting them to work will help you get past all the little things like function naming and form ID naming etc. THEN you can move on to some complex form element with its own #theme function. You probably have to theme that complex form element by overriding the template, not by adding a #prefix and #suffix. Or, if this really is a field widget, then by writing your own custom widget.

6
  • 1
    Not all form elements respect #prefix and #suffix this is incorrect. #prefix accepts all elements. Perhaps you confused it with [#field_prefix]**(api.drupal.org/api/drupal/…)
    – No Sssweat
    Oct 11, 2020 at 21:47
  • If I inspect the html I see this for the prefix/suffix tags: [#prefix] =\u0026gt; \u003Cdiv\u003E\n [#suffix] =\u0026gt; \u003C\/div\u003E\n so I think you're right regarding the first point, thanks for that! Regarding the 2nd point, the form element parent_field_idea is a Autocomplete custom field which reference content types. I'm not using any custom themes (just the default one from drupal).
    – quant
    Oct 11, 2020 at 21:47
  • @quant I can tell you're using an entity reference field based on the target in the dump. I was able to replicate your issue, for whatever reason dpm( shows empty but when using kint( it shows up. I guess its a bug of dpm. My answer has the correct solution.
    – No Sssweat
    Oct 11, 2020 at 21:55
  • No, I really mean #prefix and #suffix do not work, and certainly do not work the same, for all render elements. The fundamental problem is that these are defined on the RenderElement class, but each individual form element does NOT inherit all the capabilities of the parent RenderElement because the Form API is built on associative arrays, NOT on classes, so it's up to each individual element implementation to properly handle everything defined by the parent class. And that does not always happen.
    – anonymous
    Oct 11, 2020 at 22:00
  • 1
    @No Sssweat Yes I agree that it doesn't appear to be the problem for the specific case of an entity reference field, it was just a potential problem and a starting point for debugging the issue. As I said there were two problems which were confusing the poster. Once the first was sorted then it appears the second was easily fixed by removing the 'style' as you recommended. That would not necessarily be sufficient in every case, but it was the correct answer here for the second problem.
    – anonymous
    Oct 11, 2020 at 22:17

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.