0

I have a form containing the following element:

  $form['check'] = array(
    '#type' => 'checkbox',
    '#title' => t('Test'),
    '#ajax' => array(
      'callback' => array($this, 'updateTestCallback'),
      'event' => 'change',
    ),
  );

With the following simple Ajax handler:

  public function updateTestCallback(array $form, FormStateInterface $form_state) {
    $triggering_element = $form_state->getTriggeringElement();
    $ajax_response = new AjaxResponse();
    $ajax_response->addCommand(new AlertCommand($triggering_element['#name']." = ".$triggering_element['#value']));
    return $ajax_response;
  }

This works perfectly when the control is on a plain form, but I want to have a checkbox on every row of a table, and this is where I'm having problems.

I tried using a #table entity, and the checkboxes display correctly, but the Ajax callback event only gets called on every other click (i.e. one click it works, the next nothing happens, the next it works again).

So I should the problem might stem from from the #table entity, so I changed the form to build the table manually using #prefix and #suffix containing the table elements, but this is behaving exactly the same way, so it seems to be the presence of the table elements rather than the way they were generated that's causing the problem.

Here's the current build_form function:

  public function buildForm(array $form, FormStateInterface $form_state, $mid = NULL, $key = NULL) {
    $form = array(
      '#prefix' => '<div id="memberform">',
      '#suffix' => '</div>',
    );

    // Test checkboxes outside table:    
    $form['check'] = array(
      '#type' => 'checkbox',
      '#title' => t('Test'),
      '#ajax' => array(
        'callback' => array($this, 'updateTestCallback'),
        'event' => 'change',
      ),
    );
    $form['check2'] = array(
      '#type' => 'checkbox',
      '#title' => t('Test2'),
      '#ajax' => array(
        'callback' => array($this, 'updateTestCallback'),
        'event' => 'change',
      ),
    );

    $form['table'] = array(
      '#prefix' => '<table id="member-list">',
      '#suffix' => '</table>',
    );      

    $form['table']['tbody'] = array(
      '#prefix' => '<tbody>',
      '#suffix' => '</tbody>',
    );
    // Retrieve data and builr one table row per record.
    foreach ($entries = MyStorage::myListLoad() as $entry) {
      $mid = $entry['mid'];
      $is_paid = $entry['is_paid'];
      $row = array(
        '#prefix' => '<tr>',
        '#suffix' => '</tr>',
      );
      $row['first_name'] = array(
        '#markup' => SafeMarkup::checkPlain($entry['first_name']),
        '#prefix' => '<td>',
        '#suffix' => '</td>',
      );
      $row['last_name'] = array(
        '#markup' => SafeMarkup::checkPlain($entry['last_name']),
        '#prefix' => '<td>',
        '#suffix' => '</td>',
      );
      $row["is_approved_".$mid] = array(
        //'#attributes' => array('name' => 'is_approved_'.$mid, 'id' => 'edit_is_approved_'.$mid),
        '#type' => 'checkbox',
        '#title' => t('Is Approved'),
        '#title_display' => 'invisible',
        '#ajax' => array(
          'callback' => array($this, 'updateTestCallback'),
          'event' => 'change',
        ),
        '#prefix' => '<td>',
        '#suffix' => '</td>',
      );
      $form['table']['tbody'][$mid] = $row;
    }
    return $form;
  }

Has anyone come across this problem, and can you suggest a way around it?

James

  • You are missing the ajax wrapper. – user21641 Jan 16 '16 at 20:18
  • I don't believe a wrapper is required in this case, because updateTestCallback builds an AjaxResponse() object. The AlertCommand() object causes a JavaScript alert box to be displayed on the client, but does not replace anything on the form. – James Shields Jan 17 '16 at 9:28
  • your are correct. – user21641 Jan 17 '16 at 12:15
1

It's hard to tell without seeing some code of the table. There is probably an error in the structure of the form array. All form elements must have the correct key in the array, which correspondent with the #name. And this keys and names must be unique in the whole form. If you put a form element in a table, use an extra layer to give the element the right key.

  • Thanks for that. I've updated the question to include the (slightly shortened) build_form method. I had tried setting [#tree] = true on the table to give each checkbox a unique ID, but that seemed to cause the callback function to not get the control name, so I changed the checkbox to be called $row["is_approved_".$mid], to ensure every checkbox has a unique name. – James Shields Jan 17 '16 at 9:40
  • Put '#name' => "is_approved_".$mid,in the checkbox array and try again. – 4k4 Jan 17 '16 at 11:28
  • Just tried this, but unfortunately it doesn't seem to make any difference to the way the form behaves. When the Ajax event gets called, it does get called correctly, it's just that only every second click calls the event. On the other clicks, the form does appear to be processing an Ajax update, it's just the event doesn't get called. Is #name a new attribute in D8? In D7 you had to set it through #attributes. – James Shields Jan 17 '16 at 23:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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