Score:0

Custom field Widget multiple values as a Table

gs flag

Hello I created a custom field type and field widget with multiple values, one entity reference and two checkboxes.

Everything's was working well, but to improve the usability of the fields, I would like to display the fields as a table, and that is when my problem begins.

I tried changing the field widget to be like a table, that visually worked to me, but now I will have problem because the keys of each field will be different than the defined in the field type, any suggestions in how to handle with this problem? If i roll back and remove the 'multiple_route' key to create the table and set the $elements[''] like i orignally started the fields are saving correctly.

FieldWidgetType.php

class MultipleRouteWidgetType extends WidgetBase {

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

    $element['multiple_route'] = [
      '#type' => 'table',
      '#header' => [
        t(''),
        t('checkbox 1'),
        t('checkbox 2'),
      ],
    ];

    $route_value = NULL;
    if (isset($items[$delta]->route)) {
      $route_value = \Drupal::entityTypeManager()->getStorage('node')->load(intval($items[$delta]->route));
    }

    $element['multiple_route'][0]['route'] = $element + [
      '#title' => t('Page'),
      '#type' => 'entity_autocomplete',
      '#target_type' => 'node',
      '#required' => TRUE,
      '#empty_value' => NULL,
      '#default_value' => $route_value,
    ];

    $element['multiple_route'][0]['checkbox_1'] = [
      '#title' => t('checkbox 1'),
      '#type' => 'checkbox',
      '#empty_value' => 0,
      '#default_value' => isset($items[$delta]->checkbox_1) ? $items[$delta]->checkbox_1 : 0,
    ];

    $element['multiple_route'][0]['checkbox_2'] = [
      '#title' => t('checkbox_2.'),
      '#type' => 'checkbox',
      '#default_value' => isset($items[$delta]->checkbox_2) ? $items[$delta]->checkbox_2 : 0,
    ];

    return $element;
  }

}

Visually this is my goal and worked: enter image description here

But now the fields will not save because the keys are different than the definied in the schema, resulting in this error:

The website encountered an unexpected error. Please try again later. Error: Cannot create references to/from string offsets in Drupal\Component\Utility\NestedArray::setValue() (line 155 of core/lib/Drupal/Component/Utility/NestedArray.php). Drupal\Component\Utility\NestedArray::setValue(Array, Array, NULL) (Line: 1254)

Field Type.php property definitions and schema:

class RouteFieldType extends FieldItemBase {

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['route'] = DataDefinition::create('integer')
      ->setLabel(new TranslatableMarkup('Page'))
      ->addConstraint('RouteConstraint', ['fieldName' => $field_definition])
      ->setRequired(TRUE);

    $properties['checkbox_1'] = DataDefinition::create('boolean')
      ->setLabel(new TranslatableMarkup('checkbox_1'));

    $properties['checkbox_2'] = DataDefinition::create('boolean')
      ->setLabel(new TranslatableMarkup('checkbox_2'));

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    $schema = [
      'columns' => [
        'route' => [
          'type' => 'int',
          'unsigned' => TRUE,
          'not null' => TRUE,
        ],
        'checkbox_1' => [
          'type' => 'int',
          'size' => 'tiny',
          'unsigned' => TRUE,
          'not null' => TRUE,
          'default' => 0,
        ],
        'checkbox_2' => [
          'type' => 'int',
          'size' => 'tiny',
          'unsigned' => TRUE,
          'not null' => TRUE,
          'default' => 0,
        ],
      ],
    ];

    return $schema;
  }
Score:1
cn flag

Your widget needs to massage the form values. Like this core widget which restructures the array by removing one level:

/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php

  /**
   * {@inheritdoc}
   */
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
    // Since file upload widget now supports uploads of more than one file at a
    // time it always returns an array of fids. We have to translate this to a
    // single fid, as field expects single value.
    $new_values = [];
    foreach ($values as &$value) {
      foreach ($value['fids'] as $fid) {
        $new_value = $value;
        $new_value['target_id'] = $fid;
        unset($new_value['fids']);
        $new_values[] = $new_value;
      }
    }

    return $new_values;
  }
Score:0
gs flag

Thank you.

Before that to quick solve the issue and because my task was late, i emulate the form table with a markup on the suffix and prefix on the widget fields and returned the keys to the root of structure of $element:

public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $prefix = '<table class="responsive-enabled">
      <thead>
        <tr>
          <th></th>
          <th>' . t('Checbox 1') . '</th>
          <th>' . t('Checbox 2') . '</th>
        </tr>
      </thead>
      <tbody>
        <tr class="odd">
          <td>
    ';

    $route_value = NULL;
    if (isset($items[$delta]->route)) {
      $route_value = \Drupal::entityTypeManager()->getStorage('node')->load(intval($items[$delta]->route));
    }

    $element['route'] = $element + [
      '#prefix' => $prefix,
      '#suffix' => '</td>',
      '#title' => t('Page'),
      '#type' => 'entity_autocomplete',
      '#target_type' => 'node',
      '#required' => TRUE,
      '#empty_value' => NULL,
      '#default_value' => $route_value,
    ];

    $element['checkbox_1'] = [
      '#prefix' => '<td>',
      '#suffix' => '</td>',
      '#title' => t('c'),
      '#title_display' => 'invisible',
      '#type' => 'checkbox',
      '#empty_value' => 0,
      '#default_value' => isset($items[$delta]->checkbox_1) ? $items[$delta]->checkbox_1 : 0,
    ];

    $element['checkbox_2'] = [
      '#prefix' => '<td>',
      '#suffix' => '</td></tr></tbody></table>',
      '#title' => t('c.'),
      '#title_display' => 'invisible',
      '#type' => 'checkbox',
      '#empty_value' => 0,
      '#default_value' => isset($items[$delta]->checkbox_2) ? $items[$delta]->checkbox_2 : 0,
    ];

    return $element;
  }
I sit in a Tesla and translated this thread with Ai:

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.