How to change checkboxes options with ajax callback I'm sorry if this is repeated somewhere, but I really did try to look and couldn't come up with anything.

My scenario:

I have a form, and within that form I have a div/wrapper, with two elements in it: a) a text field and b) a checkboxes element

I have two different elements that change the values/options of those using the #ajax attribute


1) a textfield that, when changed, updates the value in a) and populates the options in b)
2) a button that's pressed (after the user enters some data) that adds an option to b)


1) works fine
2) semi-works; when you add (x), it doesn't show up (though the data gets stored in the DB fine). If you immediately add (y), (x) shows up in the options, but not (y). Add (z), and (y) shows up, and so on; so, it's always one back....

1) and 2) use different callback functions, but utilize the same wrapper

The form:

function waiver_create_waiver($form, &amp;$form_state)  { 
  $form['id'] = array(
    '#type' =&gt; 'textfield',
    '#title' =&gt; 'ID',
    '#ajax' =&gt; array(
      'callback' =&gt; '_update_user_info',
      'wrapper' =&gt; 'user-info-wrapper',
    ),
  );
  $form['user_info_div'] = array(
    '#prefix' =&gt; '&lt;div id="user-info-wrapper"&gt;',
    '#suffix' =&gt; '&lt;/div&gt;',  
  );
  $form['user_info_div']['username'] = array(
    '#type' =&gt; 'textfield',
    '#title' =&gt; 'User Name',
  );

  $id = trim($form_state['values']['id']);
  $user = user_load_by_name($id);

  // Create the checkboxes
  $user_hardware = _get_users_hardware($user);
  $hardware_options = array();
  foreach($user_hardware as $h)  {
    $hardware_options[$h['hid']] = $h['hardware_type'] . ' (' . $h['manufacturer'] . ') ' . 'S/N: ' . $h['serial_number'] . ', Last Seen: ' . date('D, n/d/Y g:i a', $h['last_seen']);
  }
  $form['user_info_div']['selected_hardware_checkboxes'] = array(
    '#type' =&gt; 'checkboxes',
    '#title' =&gt; t('Select Registered Hardware'),
    '#options' =&gt; $hardware_options,
  );

//...some textfields and selects for entering hardware info, removed for brevity...

$form['register_hardware_fieldset']['register_button'] = array(
  '#type' =&gt; 'button',
  '#value' =&gt; 'Register New User Hardware',
  '#ajax' =&gt; array(
    'callback' =&gt; '_update_hardware',
    'wrapper' =&gt; 'user-info-wrapper',
   ),
);

return $form;
}


Callback for 1) $form['id']['#ajax']

function _update_user_info($form, &amp;$form_state) {
  $id = trim($form_state['values']['id']);
  $ldap_search = ldap_connector_query_ldap(trim($id)); // This is a custom LDAP query function
  $form['user_info_div']['username']['#value'] = ucwords(strtolower($ldap_search['cn'][0]));

  return $form['user_info_div'];
}


Callback for 2) $form['register_hardware_fieldset']['register_button']['#ajax']

function _update_hardware($form, &amp;$form_state)  {
  $id = trim($form_state['values']['id']);
  if($id != NULL &amp;&amp; $id != '')  {
    $user = user_load_by_name($id);
    // If the user isn't on the site yet, create an account for them
    if(!$user)  {
      $new_user = array(
          'name' =&gt; $id,
          'pass' =&gt; user_password(),
          'mail' =&gt; $id . '@email.com',
          'init' =&gt; $id . '@email.com',
          'status' =&gt; 1, // Active user
          'roles' =&gt; array(DRUPAL_AUTHENTICATED_RID =&gt; "authenticated user"),
      );
      $user = user_save(NULL, $new_user);
    }

// ...Find $hardware_type and $manufacturer from elements cut from above for brevity...

    // Insert the hardware in to the database...
    db_insert('waiver_hardware')-&gt;fields(array(
      'user_id' =&gt; $user-&gt;uid,
      'date_added' =&gt; REQUEST_TIME,
      'hardware_type' =&gt; $hardware_type,
      'manufacturer' =&gt; $manufacturer,
      ))
      -&gt;execute();
  }
  return $form['user_info_div'];
}


EDIT: I've tried adding the options to the checkboxes element within the callback, as well as making the $form a reference call, but to no avail.
I think the problem is simply a matter of the order the functions are run in.

The AJAX callback will run after the form has been built. So even though you're adding the new item to the database, the code to build the checkbox options (from $user_hardware = _get_users_hardware($user); downwards) has already run. Since it's already run, the new DB item won't be shown until the next time the page is loaded.

If that is the problem the fix would be as simple as adding a bit of code to the end of your _update_hardware() function, to populate the new options:

// ...

// Insert the hardware in to the database...
db_insert('waiver_hardware')-&gt;fields(array(
  'user_id' =&gt; $user-&gt;uid,
  'date_added' =&gt; REQUEST_TIME,
  'hardware_type' =&gt; $hardware_type,
  'manufacturer' =&gt; $manufacturer,
))-&gt;execute();

$hardware_options = array();
foreach($user_hardware as $h)  {
  $hardware_options[$h['hid']] = $h['hardware_type'] . ' (' . $h['manufacturer'] . ') ' . 'S/N: ' . $h['serial_number'] . ', Last Seen: ' . date('D, n/d/Y g:i a', $h['last_seen']);
}

$form['user_info_div']['selected_hardware_checkboxes']['#options'] = $hardware_options;

return $form['user_info_div'];