Score:0

Custom webform element saves value as array

ng flag

I have created a custom webform element based on the webform example element. https://github.com/drupalprojects/webform/tree/8.x-5.x/modules/webform_example_element

It is still a simple text field, like the example but does some calculations form the current formid submissions.
Somhow every value entered in my text field shows up as "Array" (basically as the word "Array" in the submissions. I did a dpm($element['#value']) in the validation handler of the element:
enter image description here

This does not happen with the example. It seems like I messed something up - maybe somone could shed a light on where to look for? Where is controlled in which format an input value is saved?

I have added some custom twig code - could this be the reason?

    <div class="custom-element">
  <label for="{{ element['#webform_id'] }}-{{ element['#key'] }}">{{ element['#title'] }}</label>
  <div class="custom-element-textfield">{{ dd(element) }}
    <input type="text" name="{{ element['#name'] }}[value]" value="{{ element['#value'] }}" class="form-control">
    <p>Available Seats: {{ element['#maxseats'] }}</p>
  </div>
</div>

Here is the yaml of the form:`

uuid: 7349e6bd-4112-4b4b-8b8c-bf904e3a32c6
langcode: de
status: open
dependencies: {  }
weight: 0
open: null
close: null
uid: 1
template: false
archive: false
id: platzreservierung
title: Platzreservierung
description: ''
category: ''
elements: |-
  book_seats:
    '#type': my_webform_seatbooking_element
    '#title': 'Plätze buchen (Element)'
    '#multiple': false
    '#placeholder': '[webform:element:book_seats:maxseats]'
    '#maxseats': '100'
  test:
    '#type': webform_example_element
    '#title': test
    '#multiple': false
css: ''
javascript: ''
settings:
  ajax: false
  ajax_scroll_top: form
  ajax_progress_type: ''
  ajax_effect: ''
  ajax_speed: null
  page: true
  page_submit_path: ''
  page_confirm_path: ''
  page_theme_name: ''
  form_title: both
  form_submit_once: false
  form_open_message: ''
  form_close_message: ''
  form_exception_message: ''
  form_previous_submissions: true
  form_confidential: false
  form_confidential_message: ''
  form_disable_remote_addr: false
  form_convert_anonymous: false
  form_prepopulate: false
  form_prepopulate_source_entity: false
  form_prepopulate_source_entity_required: false
  form_prepopulate_source_entity_type: ''
  form_unsaved: false
  form_disable_back: false
  form_submit_back: false
  form_disable_autocomplete: false
  form_novalidate: false
  form_disable_inline_errors: false
  form_required: false
  form_autofocus: false
  form_details_toggle: false
  form_reset: false
  form_access_denied: default
  form_access_denied_title: ''
  form_access_denied_message: ''
  form_access_denied_attributes: {  }
  form_file_limit: ''
  form_attributes: {  }
  form_method: ''
  form_action: ''
  share: false
  share_node: false
  share_theme_name: ''
  share_title: true
  share_page_body_attributes: {  }
  submission_label: ''
  submission_exception_message: ''
  submission_locked_message: ''
  submission_log: false
  submission_excluded_elements: {  }
  submission_exclude_empty: false
  submission_exclude_empty_checkbox: false
  submission_views: {  }
  submission_views_replace: {  }
  submission_user_columns: {  }
  submission_user_duplicate: false
  submission_access_denied: default
  submission_access_denied_title: ''
  submission_access_denied_message: ''
  submission_access_denied_attributes: {  }
  previous_submission_message: ''
  previous_submissions_message: ''
  autofill: false
  autofill_message: ''
  autofill_excluded_elements: {  }
  wizard_progress_bar: true
  wizard_progress_pages: false
  wizard_progress_percentage: false
  wizard_progress_link: false
  wizard_progress_states: false
  wizard_start_label: ''
  wizard_preview_link: false
  wizard_confirmation: true
  wizard_confirmation_label: ''
  wizard_auto_forward: true
  wizard_auto_forward_hide_next_button: false
  wizard_keyboard: true
  wizard_track: ''
  wizard_prev_button_label: ''
  wizard_next_button_label: ''
  wizard_toggle: false
  wizard_toggle_show_label: ''
  wizard_toggle_hide_label: ''
  wizard_page_type: container
  wizard_page_title_tag: h2
  preview: 0
  preview_label: ''
  preview_title: ''
  preview_message: ''
  preview_attributes: {  }
  preview_excluded_elements: {  }
  preview_exclude_empty: true
  preview_exclude_empty_checkbox: false
  draft: none
  draft_multiple: false
  draft_auto_save: false
  draft_saved_message: ''
  draft_loaded_message: ''
  draft_pending_single_message: ''
  draft_pending_multiple_message: ''
  confirmation_type: page
  confirmation_url: ''
  confirmation_title: ''
  confirmation_message: ''
  confirmation_attributes: {  }
  confirmation_back: true
  confirmation_back_label: ''
  confirmation_back_attributes: {  }
  confirmation_exclude_query: false
  confirmation_exclude_token: false
  confirmation_update: false
  limit_total: null
  limit_total_interval: null
  limit_total_message: ''
  limit_total_unique: false
  limit_user: null
  limit_user_interval: null
  limit_user_message: ''
  limit_user_unique: false
  entity_limit_total: null
  entity_limit_total_interval: null
  entity_limit_user: null
  entity_limit_user_interval: null
  purge: none
  purge_days: null
  results_disabled: false
  results_disabled_ignore: false
  results_customize: false
  token_view: false
  token_update: false
  token_delete: false
  serial_disabled: false
access:
  create:
    roles:
      - anonymous
      - authenticated
    users: {  }
    permissions: {  }
  view_any:
    roles: {  }
    users: {  }
    permissions: {  }
  update_any:
    roles: {  }
    users: {  }
    permissions: {  }
  delete_any:
    roles: {  }
    users: {  }
    permissions: {  }
  purge_any:
    roles: {  }
    users: {  }
    permissions: {  }
  view_own:
    roles: {  }
    users: {  }
    permissions: {  }
  update_own:
    roles: {  }
    users: {  }
    permissions: {  }
  delete_own:
    roles: {  }
    users: {  }
    permissions: {  }
  administer:
    roles: {  }
    users: {  }
    permissions: {  }
  test:
    roles: {  }
    users: {  }
    permissions: {  }
  configuration:
    roles: {  }
    users: {  }
    permissions: {  }
handlers: {  }
variants: {  }

the /src/plugin/WebformElement/MyWebformSeatbookingElement.php

<?php

namespace Drupal\my_webform_seatbooking_element\Plugin\WebformElement;

use Drupal\Core\Form\FormStateInterface;
use Drupal\webform\Plugin\WebformElementBase;
use Drupal\webform\WebformSubmissionInterface;

/**
 * Provides a 'my_webform_seatbooking_element' element.
 *
 * @WebformElement(
 *   id = "my_webform_seatbooking_element",
 *   label = @Translation("Webform seat booking element"),
 *   description = @Translation("Provides a webform element for booking seats."),
 *   category = @Translation("Custom elements"),
 * )
 *
 * @see \Drupal\my_webform_seatbooking_element\Element\HwWebformSeatbookingElement
 * @see \Drupal\webform\Plugin\WebformElementBase
 * @see \Drupal\webform\Plugin\WebformElementInterface
 * @see \Drupal\webform\Annotation\WebformElement
 */
class HwWebformSeatbookingElement extends WebformElementBase {

  /**
   * {@inheritdoc}
   */
  protected function defineDefaultProperties() {
    // Here you define your webform element's default properties,
    // which can be inherited.
    //
    // @see \Drupal\webform\Plugin\WebformElementBase::defaultProperties
    // @see \Drupal\webform\Plugin\WebformElementBase::defaultBaseProperties
    return [
      'multiple' => '',
      'size' => '',
      'minlength' => '',
      'maxlength' => '',
      'placeholder' => '',
        'maxseats' => '',
    ] + parent::defineDefaultProperties();
  }

  /* ************************************************************************ */

  /**
   * {@inheritdoc}
   */
  public function prepare(array &$element, WebformSubmissionInterface $webform_submission = NULL) {
    parent::prepare($element, $webform_submission);

    //dpm('be-prepared');
    //dpm($element);

    // Get the current webform ID.
    $webform_id = $element['#webform_id'];

    // Get the current element's machine name.
    $machine_name = $element['#webform_key'];

    // Get all submissions for the current webform.
    $webform_storage = \Drupal::entityTypeManager()->getStorage('webform_submission');
    $submissions = $webform_storage->loadByProperties(['webform_id' => $webform_id]);

    // Sum up the values of all submissions for the current element.
    $total = 0;

    if (!empty($submissions)) {
      foreach ($submissions as $submission) {
        $data = $submission->getData();
        if (isset($data[$machine_name])) {
          $total += $data[$machine_name];
        }
      }
    }

    //dpm($total);

    // Add the total to the element's attributes.
    //$element['data-total'] = $total;
    // Here you can customize the webform element's properties.
    // You can also customize the form/render element's properties via the
    // FormElement.
    //
    // @see \Drupal\my_webform_seatbooking_element\Element\WebformExampleElement::processWebformElementExample
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form = parent::form($form, $form_state);
    // Here you can define and alter a webform element's properties UI.
    // Form element property visibility and default values are defined via
    // ::defaultProperties.
    //
    // @see \Drupal\webform\Plugin\WebformElementBase::form
    // @see \Drupal\webform\Plugin\WebformElement\TextBase::form
    return $form;
  }

}



the /src/Element/MyWebformSeatBookingElement.php

<?php

namespace Drupal\my_webform_seatbooking_element\Element;

use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\FormElement;
use Drupal\Core\Form\FormStateInterface;

/**
 * Provides a 'my_webform_seatbooking_element'.
 *
 * Webform elements are just wrappers around form elements, therefore every
 * webform element must have correspond FormElement.
 *
 * Below is the definition for a custom 'my_webform_seatbooking_element' which just
 * renders a simple text field.
 *
 * @FormElement("my_webform_seatbooking_element")
 *
 * @see \Drupal\Core\Render\Element\FormElement
 * @see https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21FormElement.php/class/FormElement
 * @see \Drupal\Core\Render\Element\RenderElement
 * @see https://api.drupal.org/api/drupal/namespace/Drupal%21Core%21Render%21Element
 * @see \Drupal\my_webform_seatbooking_element\Element\HwWebformSeatbookingElement
 */
class HwWebformSeatbookingElement extends FormElement {

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    $class = get_class($this);
    return [
      '#input' => TRUE,
      '#size' => 60,
      '#process' => [
        [$class, 'processWebformElementSeatbooking'],
        [$class, 'processAjaxForm'],
      ],
      '#element_validate' => [
        [$class, 'validateWebformSeatbookingElement'],
      ],
      '#pre_render' => [
        [$class, 'preRenderWebformSeatbookingElement'],
      ],
      '#theme' => 'input__my_webform_seatbooking_element',
      '#theme_wrappers' => ['form_element'],
    ];
  }

  /**
   * Processes a 'my_webform_seatbooking_element' element.
   */
  public static function processWebformElementSeatbooking(&$element, FormStateInterface $form_state, &$complete_form) {
    // Here you can add and manipulate your element's properties and callbacks.


    return $element;
  }

  /**
   * Webform element validation handler for #type 'my_webform_seatbooking_element'.
   */
  public static function validateWebformSeatbookingElement(&$element, FormStateInterface $form_state, &$complete_form) {
    // Here you can add custom validation logic.
    //dpm($element['#value']);

  }

  /**
   * Prepares a #type 'email_multiple' render element for theme_element().
   *
   * @param array $element
   *   An associative array containing the properties of the element.
   *   Properties used: #title, #value, #description, #size, #maxlength,
   *   #placeholder, #required, #attributes.
   *
   * @return array
   *   The $element with prepared variables ready for theme_element().
   */
  public static function preRenderWebformSeatbookingElement(array $element) {
    $element['#attributes']['type'] = 'text';
    Element::setAttributes($element, ['id', 'name', 'value', 'size', 'maxlength', 'placeholder']);
    static::setAttributes($element, ['form-text', 'webform-seatbooking-element']);
    return $element;
  }

}

I get a Notice:


    Notice: Array to string conversion in Drupal\webform\WebformSubmissionStorage->saveData() (line 1350 of modules/contrib/webform/src/WebformSubmissionStorage.php).
    ...
    Drupal\Core\Form\FormSubmitter->executeSubmitHandlers(Array, Object) (Line: 52)
    Drupal\Core\Form\FormSubmitter->doSubmitForm(Array, Object) (Line: 592)
    Drupal\Core\Form\FormBuilder->processForm('webform_submission_platzreservierung_add_form', Array, Object) (Line: 320)

Sorry for all that stuff. Maybe it helps.

id flag
There is a `dd` in that code. Surely that could be an issue. Post the YAML of the entire form without debugging code to get answers.
ng flag
removed the dd - hope the code helps a bit further.
Score:0
ng flag

The mistake was in the twig code. Somehow I added an [value] to the "name" attribute of the input field.

<input type="text" name="{{ element['#name'] }}[value]" value="{{ element['#value'] }}" class="form-control"> 

Using something like this, it works.

<input type="text" name="{{ element['#name'] }}" class="form-control">
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.