0

I would like to:

1.) Add rows to a tableselect form element when a user clicks on a button.

2.) See which rows are selected when a user clicks on another button.

Part one is complete (I've only included relevant code):

public function buildForm(array $form, FormStateInterface $form_state) {

  //Tableselect
  $header = [
    'first_name' => $this->t('First Name'),
  ]

  $form['results'] = [
    '#type' => 'tableselect',
    '#title' => $this->t('Select contact to add to message list'),
    '#header' => $header,
    '#options' => [],
    '#multiple' => TRUE,
    '#empty' => $this->t('No users found'), 
    '#prefix' => '<div id="search-results">',
    '#suffix' => '</div>',
  ];

  //This button adds rows to tableselect
  $form['button_one'] = [
    '#type' => 'button',
    '#value' => $this->t('Search'),
    '#ajax' => [
      'callback' => '::contactSearch',
      'wrapper' => 'search-results',
      'effect' => 'fade',
    ],
  ];

  //This button displays $form_state in a kint
  $form['button_two'] = [
    '#type' => 'button',
    '#value' => $this->t('Debug'),
    '#ajax' => [
      'callback' => '::debugSearch',
      'wrapper' => 'search-results',
      'effect' => 'fade',
    ],
  ];

  //This is for kint
  $form['debug'] = [
    '#type' => 'container',
    '#attributes' => [
      'id' => ['debug-out'],
    ],
  ];

}

/**
 * Ajax callback for button one
 */
public function contactSearch(array &$form, FormStateInterface $form_state) {

  //The AJAX callback replaces the tableselect with one that has more rows.
  $header = [
    'first_name' => $this->t('First Name'),
  ];

  $rows = [
    1 => 'Ben',
    2 => 'Sarah',
    3 => 'Steve',
  ];

  $form['results'] = [
    '#type' => 'tableselect',
    '#title' => $this->t('Select contact to add to message list'),
    '#header' => $header,
    '#options' => $rows,
    '#multiple' => TRUE,
    '#empty' => $this->t('No users found'), 
    '#prefix' => '<div id="search-results">',
    '#suffix' => '</div>',
  ];

  //This seems to be necessary specifically for tableselect
  $form['results'] = Tableselect::processTableselect($form['results'], $form_state, $form);
  $response->addCommand(new ReplaceCommand('#search-results', $form['results']));
  return $response;

}

/**
 * Ajax callback for button two
 */
public function debugSearch(array &$form, FormStateInterface $form_state) {

  //Show current state of tableselect in a kint
  $response = new AjaxResponse();
  $debugOut = @Kint::dump($form_state->getValue('results'));
  $response->addCommand(new ReplaceCommand('#debug-out', $debugOut));
  return $response;

}

What I expect:

I expect button one to update $form so that when I click on button two, $form_state->getValue('results') will return which rows are selected.

What happens:

The button 2 AJAX function doesn't register any change to the tableselect element.

Visually, the tableselect changes as I expect after clicking button one: the extra rows are added. But the AJAX callback for button 2 doesn't see any change.

I need to know which rows are selected AFTER the tableselect has been updated.

1 Answer 1

1

On this DA issue there is a comment:

You can't change elements on callback or validateForm(), you need to put it on buildForm() method.

Which is what I was doing. I was adding rows to $form inside the AJAX callback.

So, I create a method called by buildForm() that evaluates the number on rows based on values in $form_state:

    $form['search_results']['results'] = [
      '#type' => 'tableselect',
      '#title' => $this->t('Select contact to add to message list'),
      '#header' => $header,
      '#options' => $this->getRows($form_state),
      '#multiple' => TRUE,
      '#empty' => $this->t('No users found'), 
      '#prefix' => '<div id="search-results">',
      '#suffix' => '</div>',
    ];

My understanding is that the AJAX callback will call buildForm() again, which will call my getRows() method.

The new rows in tableselect returned using this approach are available in $Form and $form_state will show which are selected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.