1

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, &$form_state)  { 
  $form['id'] = array(
    '#type' => 'textfield',
    '#title' => 'ID',
    '#ajax' => array(
      'callback' => '_update_user_info',
      'wrapper' => 'user-info-wrapper',
    ),
  );
  $form['user_info_div'] = array(
    '#prefix' => '<div id="user-info-wrapper">',
    '#suffix' => '</div>',  
  );
  $form['user_info_div']['username'] = array(
    '#type' => 'textfield',
    '#title' => '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' => 'checkboxes',
    '#title' => t('Select Registered Hardware'),
    '#options' => $hardware_options,
  );

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

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

return $form;
}

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

function _update_user_info($form, &$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, &$form_state)  {
  $id = trim($form_state['values']['id']);
  if($id != NULL && $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' => $id,
          'pass' => user_password(),
          'mail' => $id . '@email.com',
          'init' => $id . '@email.com',
          'status' => 1, // Active user
          'roles' => array(DRUPAL_AUTHENTICATED_RID => "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')->fields(array(
      'user_id' => $user->uid,
      'date_added' => REQUEST_TIME,
      'hardware_type' => $hardware_type,
      'manufacturer' => $manufacturer,
      ))
      ->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.

1

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')->fields(array(
  'user_id' => $user->uid,
  'date_added' => REQUEST_TIME,
  'hardware_type' => $hardware_type,
  'manufacturer' => $manufacturer,
))->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'];
  • I was actually doing that first, and it didn't work. To be sure though, I DID try it again--with the addition of a call to _get_users_hardware() before the foreach()--and same thing. It only adds what you enter AFTER you add the NEXT one, and so on. – UnsettlingTrend May 9 '13 at 15:35
  • Hmm strange, I'm sure I've done the same sort of thing loads of times. Just in case, try passing $form in by reference to your callback function – Clive May 9 '13 at 15:44
  • Heh, I tried that before too, but I gave it another go. To do that, I need only to change the function definition to "function _update_hardware(&$form, &$form_state)", right? If so, it also didn't change anything. Could this have ANYTHING to do with the fact that I'm sharing the wrapper between two ajax'd elements? I wouldn't think so, but just checking. (Sorry, I know I'm supposed to say everything I've tried in the initial post, but I've gone back and forth over this thing so many times, I'm only remembering it now when you mention it.) – UnsettlingTrend May 9 '13 at 16:30
  • No worries, those are just guidelines it's not always feasible to get hours of head-v-wall banging into a question ;) Yep just adding the & is all you need to do for the function sig. It shouldn't have anything to do with the multiple elements in one wrapper, I've done the same sort of thing plenty of times. I'm intrigued, I'll chuck the code in a test site and see if I can tweak it to work – Clive May 9 '13 at 16:36
  • Not multiple elements IN one wrapper (though, that is the case), multiple elements CHANGING a single wrapper; though, at different times/triggers. I don't think that's it though either, since I took the AJAX out of the first one, and the other was still behaving the same way. That would be awesome, thanks. – UnsettlingTrend May 9 '13 at 16:48

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.