Score:0

Add rows to form tableselect with AJAX

na flag

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.

Score:1
na flag

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.

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.