I have been working on that problem for a while. I read many posts everywhere on internet and I do not understand why submitting the form only saves options['merge_rows']. Nothing about options['table_fieldset'] is saved. Anyone can help?

I use #tree = TRUE.

Here is the method building the form:

  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    if ($form_state->get('section') == 'views_merge_rows') {
      $options = $this->get_options();

      if ($this->displayHandler->usesPager()) {
        $form['warning_markup'] = array(
          '#markup' => '<div class="warning messages">' . t('It is highly recommended to disable pager if you merge rows.') . '</div>',
        );
      }
      $form['#tree'] = TRUE;
      $form['#title'] .= t('Merge rows with the same content.');
      $form['merge_rows'] = array(
        '#type' => 'checkbox',
        '#title' => t('Merge rows with the same content in the specified fields'),
        '#default_value' => $options['merge_rows'],
      );
      $form['table_fieldset'] = array(
        '#id' => 'views-merge-rows-table-fieldset',
        '#title' => t('Configure merging options for the fields'),
        '#type' => 'fieldset',
      	'#tree' => TRUE,
      	'#dependency' => array('edit-options-merge-rows' => array(1)),
      );
      $header = array(
        t('Field'),
        t('Merge option'),
        t('Exclude first'),
        t('Separator'),
      );

      // Create an array of allowed columns from the data we know:
      $field_names = $this->displayHandler->getFieldLabels();
      foreach ($field_names as $field => $name) {
        $row = [];
        $safe = str_replace(array('][', '_', ' '), '-', $field);
        // Markup for the field name.
        $row[]['data'] = array('#markup' => $name,);
        // Select for merge options.
        $row[]['data'] = array(
          '#type' => 'select',
          '#options' => array(
            'merge_unique' => t('Merge unique values of this field'),
            'merge' => t('Merge values of this field'),
            'filter' => t('Use values of this field as a filter'),
            'first_value' => t('Use the first value of this field'),
            'highest_value' => t('Use the highest value of this field'),
            'count' => t('Count merged values of this field'),
            'count_unique' => t('Count merged unique values of this field'),
            'sum' => t('Sum values of this field'),
          ),
          '#default_value' => $options['field_config'][$field]['merge_option'],
        );
        $row[]['data'] = array(
          '#title' => t(''),
          '#type' => 'checkbox',
          '#default_value' => $options['field_config'][$field]['exclude_first'],
        );
        $row[]['data'] = array(
          '#id' => 'views-merge-rows-separator',
          '#title' => t('Separator:'),
          '#type' => 'textfield',
          '#size' => 10,
          '#default_value' => $options['field_config'][$field]['separator'],
          '#dependency' => array('edit-options-field-config-' . $safe . '-merge-option' => array('merge', 'merge_unique')),
        );
        $rows[] = $row;
      }

      $table = array(
        '#type' => 'table',
        '#header' => $header,
        '#rows' => $rows,
      );

      $form['table_fieldset']['table'] = array(
        '#type' => 'markup',
        '#markup' => render($table),
      );
    }
  }

Comments

jaypan’s picture

You've set table_fieldset to be a tree, so the values of the table will be retrievable using $form_state->getValue(['table_fieldset', 'table']);

Contact me to contract me for D7 -> D10/11 migrations.

dgagne’s picture

@Jaypan

I tried your proposed solution and it doesn't work. I also xDebug to see what is submitted and I do not find anything in $form_state['values'] related to ['table_fieldset']['table']. It is really like everything in the fieldset doesn't return any value.

Below is my submit method, as modified:

  public function submitOptionsForm(&$form, FormStateInterface $form_state) {
    foreach ($form_state->getValues('options') as $option => $value) {
      $this->displayHandler->setOption($option, $value);
    }
    foreach ($form_state->getValue(['table_fieldset', 'table']) as $option => $value) {
    	$this->displayHandler->setOption($option, $value);
    }
  }

Any other idea ?

jaypan’s picture

Yeah, I see your problem actually. You are building your table with your form elements, then calling render inside your form defintion to render your table, and adding it as markup. This means the form elements are not registered as part of your form, since they have already been rendered before the form has ever been processed.

If you want to have form elements inside the table, you need to build your form elements inside a container, then call a theme on that container, building your theme inside the theme callback.

Here's a tutorial I wrote for Drupal 7 - it's essentially the same for Drupal 8: http://www.jaypan.com/tutorial/themeing-drupal-7-forms-including-css-and-js

Contact me to contract me for D7 -> D10/11 migrations.

hibadev’s picture

did you find a solution for this problem ?i want to get a table from form state get value and it give me an empty one