Just started developing with Drupal forms and have this question:-

How do I implement one drop-down select list based on the input to another? I have a country selection field followed by a province selection field.

I have got as far as invoking a JS function when the country field changes and the JS function can retrieve the value entered in the country field.

I have the required data in the database, so my question becomes how, from Javascript do I invoke the PHP to read the data and then return that to the province selection field?

I have searched around here and hierarchical_select looked promising but I don't think that works with dynamic data. This seems to have cropped up many times but I can't find a full step-by-step account of how to do this.

Any help would be much appreciated.

Thanks

fudge

Comments

bwv’s picture

Please delete. Apologies.

afingeroffudge’s picture

Thanks for your reply but, as I said in the original post, I don't think hierarchical_select works with dynamic data.

All the examples are based on menu and taxonomy, so everything is pre-loaded.

I need to be able to get the list of states/provinces from the datbase for the specific country selected.

Thanks
fudge

fidot’s picture

I have just completed similar functionality for a client. This is what I did.

Firstly, I added the following code to the form's country field

'#ahah' => array
  'path' => 'addresses/getstates',
  'wrapper' => 'ahah-wrapper',
  'progress' => array('type' => 'bar', 'message' => t('Please wait')),
),

and then added prefix and suffix to the form's state field so that the field is enclosed within the "ahah-wrapper" div.

I then added the following to the module's hook_menu

$items['addresses/getstates'] = array
  'page callback' => 'addresses_getstates',
  'type' => MENU_CALLBACK,
  'access arguments' => array('access content'),
);

and defined the following function within the module:-

function addresses_getstates() {
  // Get the selected country code from the POSted data.
  $country_code = $_POST['details']['address']['country'];
  // Recreate the replacement state field exactly as originally declared but with different select options.
  $form['state'] = array(
    '#type' => 'select',
    '#title' => t('State'),
    '#options' => addresses_addresses_province_get($country_code),
    '#required' => true, 
    '#prefix' => '<div id="ahah-wrapper">',
    '#suffix' => '</div>'
  );
  // Rebuild form object
  $output = ahah_render($form, 'state');
  // Render form output as JSON.
  print drupal_to_js(array('data' => $output, 'status' => true));
  // Exit to avoid rendering the theme layer.
  exit();
}

Note that the above field definition is exactly the same as the original field except with different select options courtesy of function addresses_addresses_province_get.

The last piece of the jigsaw is

// a help function pulled from Nick Lewis's blog to alter the form
// see: http://www.nicklewis.org/node/967
// NOTE: based on poll module, see: poll_choice_js() function in poll.module
function ahah_render($fields, $name) {
  $form_state = array('submitted' => FALSE);
  $form_build_id = $_POST['form_build_id'];
  // Add the new element to the stored form. Without adding the element to the
  // form, Drupal is not aware of this new elements existence and will not
  // process it. We retreive the cached form, add the element, and resave.
  $form = form_get_cache($form_build_id, $form_state);
  $form[$name] = $fields;
  form_set_cache($form_build_id, $form, $form_state);
  $form += array(
    '#post' => $_POST,
    '#programmed' => FALSE,
  );
  // Rebuild the form.
  $form = form_builder($_POST['form_id'], $form, $form_state);

  // Render the new output.
  $new_form = $form[$name];
  return drupal_render($new_form);
}

Hope that makes sense and helps you out.

Terry

fidot’s picture

In fact the above solution is fine for adding a new field. What I wanted to do was to replace the existing field so the callback function code became:-


function addresses_getstates() {
// The form is generated in an include file which we need to include manually.
include_once 'modules/node/node.pages.inc';

// Get cached version of form.
$form_state = array('storage' => NULL, 'submitted' => FALSE);
$form_build_id = $_POST['form_build_id'];
$form = form_get_cache($form_build_id, $form_state);

// Preparing for #5.
$args = $form['#parameters'];
$form_id = array_shift($args);
$form_id = $args;
$form_state['post'] = $form['#post'] = $_POST;
$form['#programmed'] = $form['#redirect'] = FALSE;

// Step #5.
//drupal_process_form($form_id, $form, $form_state);

// Get the selected country code from the POSTed data.
$country_code = $_POST['details']['address']['country'];

// Update the field with the new options and save the form to cache.
$form['form']['details']['address']['state']['#options'] = addresses_addresses_province_get($country_code);
form_set_cache($form_build_id, $form, $form_state);

$form += array(
'#post' => $_POST,
'#programmed' => FALSE,
);

$form = form_builder('booking_form', $form, $form_state);

$subform = $form['form']['details']['address']['state'];
$output = drupal_render($subform);

// Render form output as JSON
print drupal_json(array('data' => $output, 'status' => true));
// Exit to avoid rendering the theme layer.
exit();
}

HTH
Terry