0

Is it possible to create a custom field widget or use hook_form_alter() to show multiple form elements that, used together, can set the value of a single field?

For example, I added a textfield Personalization.

But on the entity edit form, I do not want users to edit the Personalization field directly; instead, I want them to check some boxes and fill in some text fields, and then I want to process that data and set the value of the Personalization field as a result of that processing. How do I set up the extra form elements and use them to set the value of the field?

I tried to do this by override the textarea FieldWidget:

/MYMODULE/src/Plugin/Field/FieldWidget/TextareaPersonalizationWidget.php

<?php

namespace Drupal\MYMODULE\Plugin\Field\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextareaWidget;
use Drupal\Core\Form\FormStateInterface;

/**
 * Plugin implementation of the 'text_textarea_personalization' widget.
 *
 * @FieldWidget(
 *   id = "text_textarea_personalization",
 *   module = "MYMODULE",
 *   label = @Translation("Personalization widget"),
 *   field_types = {
 *     "text_long",
 *     "string_long"
 *   }
 * )
 */
class TextareaPersonalizationWidget extends StringTextareaWidget {

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {

    $element['a']['#type'] = 'textfield';
    $element['a']['#title'] = 'Your fav animal';
    $element['a']['#maxlength'] = '255';
    $element['b']['#type'] = 'textfield';
    $element['b']['#title'] = 'Your fav fruit';
    $element['b']['#maxlength'] = '255';
    return $element;
  }

}

This causes two textfields to be shown instead of the textarea. Great! Except the value never gets saved, and I can't process the input text in hook_entity_presave(). So how can I process the input in the custom form elements and save the result in the original field?

I also tried the same thing in MYMODULE_field_widget_string_textarea_form_alter() but I had the same problem.

More details about how my site is set up

Here's my setup: I have a flag called Collection. This flag contains a textfield, Personalization. This textfield will contain pairs of strings for replacing text with JavaScript in order to customize the text on terms that have been flagged by the user.

Of course, I don't want users to type in arbitrary strings into a textfield; this would be a terrible UI. So, when a user flags a taxonomy term, I want to show some form elements that allow the user to input some options for personalization. These form elements will be different depending on which term is being flagged. For example, if flagging the Fruits term, I want to add a text field "What is your favorite fruit?" And on the Cakes term, I want to add a list of checkboxes "Choose all the types of frosting you like."

In other words, I want to define a custom form per taxonomy term that sets the value of the Personalization field, and I want to somehow embed this custom form on the flag add form.

2
  • Can you provide wireframe your field widget and what's data you want to save to another field?
    – MrD
    Commented Dec 22, 2020 at 4:05
  • @MrD I added more detail about what I am trying to achieve. Commented Dec 22, 2020 at 10:28

2 Answers 2

1

Except the value never gets saved, and I can't process the input text in hook_entity_presave().

You can add a new validate for your widget:

/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
  // Your logic code

  // Add validate for your widget.
  $element['#element_validate'] = [
    [$this, 'validate'],
  ];

  return $element;
}

And convert value before save:

/**
* Validate the fields and convert data before save.
*/
public function validate($element, FormStateInterface $form_state) {
    // Logic validate.
    // Code here

    // Convert value and set.
    // Field type text_long
    $value = [
     'value' => 'your value',
     'format' => 'your format'
    ];

    // Field type string_long
    $value = [
      'value' => 'your value'
    ];

    $form_state->setValueForElement($element, $value);
}
2
  • Thanks, but where is this validate() method that I am overriding? I tried adding the code to my TextareaPersonalizationWidget.php class, but it didn't get picked up. I checked StringTextareaWidget and WidgetBase but I couldn't find a validate() function in either one. Commented Dec 23, 2020 at 16:42
  • I updated my answer.
    – MrD
    Commented Dec 24, 2020 at 3:10
1

Have a look at https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Field%21WidgetBase.php/function/WidgetBase%3A%3AmassageFormValues/8.9.x

Massages the form values into the format expected for field values.

You may need to implement that function for your custom widget.

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.