2

I have created a content type (Geological Locations) with 2 fields, Country and States.

The Country field is a select box, and States field is a multiselect. So if i select a Country, the States multiselect should be populated based on the selected Country using AJAX.

In my custom module, i used node_form_alter() to customize the node creation form.

I have tried the below code, but it is not working as expected.

function demo_form_geological_locations_node_form_alter(&$form, &$form_state, $form_id) {
    $form['additional_settings']['#access'] = false;
    $form['field_country']['und']['#options'] = array(''=>'Select', 'india'=>'India', 'usa'=>'USA');
    $selected = isset($form_state['values']['field_country']) ? $form_state['values']['field_country'] : '';

    $form['field_country']['und']['#ajax'] = array(
          'callback' => 'ajax_states_callback',
          'wrapper' => 'states-wrapper',
    );

    $form['field_states']['#prefix'] = '<div id="states-wrapper">';
    $form['field_states']['#suffix'] = '</div>';
    $form['field_states']['und']['#options'] = ajax_get_state_options($selected);

    return $form;
}

function ajax_states_callback($form, $form_state) {
    return $form['field_states'];
}

function ajax_get_state_options($key = '') {
    $options = array(
        t('usa') => drupal_map_assoc(
          array(
            t('Florida'),
            t('Georgia'),
            t('Atlanta')
          )
        ),
        t('india') => drupal_map_assoc(
          array(
            t('Maharashtra'),
            t('Gujarat'),
            t('Karnataka')
          )
        )
    );

    if (isset($options[$key])) {
        return $options[$key];
    }
    else {
        return array();
    }
}

Can anyone tell me where i am going wrong? I am expecting the output like below screenshot, when i select the Country "India":

enter image description here

4
  • Have you considered an alternative method: Create your geo information as parent/child terms in a vocabulary, then use hierarchical select module in multi select mode. Commented Nov 10, 2014 at 8:49
  • Actually the data of Countries and States comes from an external API (hundreds of records) and i parse it to create custom arrays, so that i can use AJAX to populate States field, based on the selected Country. Commented Nov 10, 2014 at 10:15
  • Not sure if my code is not working, because i am using form_alter(). Commented Nov 12, 2014 at 6:03
  • As an update, the solution by Naveen Valecha worked out well. And I have shifted to the Dev version of the multiselect module, as the original version is giving some warnings with AJAX onchange. Commented Nov 18, 2014 at 4:52

4 Answers 4

1
+50

Awesome question with full detail.After checking I tested it on my local dev box for its fix. Use the below code for its fix. I have written the problem & its fix below.

    <?php

function demo_form_geological_locations_node_form_alter(&$form, &$form_state, $form_id) {

  $form['additional_settings']['#access'] = false;
  $form['field_country']['und']['#options'] = array(''=>'Select', 'india'=>'India', 'usa'=>'USA');
  $selected = isset($form_state['values']['field_country']) ? $form_state['values']['field_country']['und'][0]['value'] : '';
  $form['field_country']['und']['#ajax'] = array(
    'callback' => 'ajax_states_callback',
    'wrapper' => 'states-wrapper',
  );
  $form['field_states']['#prefix'] = '<div id="states-wrapper">';
  $form['field_states']['#suffix'] = '</div>';
  $form['field_states']['und']['#options'] = ajax_get_state_options($selected);
  return $form;
}

function ajax_states_callback($form, $form_state) {
  return $form['field_states'];
}

function ajax_get_state_options($key = '') {
    $options = array(
        t('usa') => drupal_map_assoc(
          array(
            t('Florida'),
            t('Georgia'),
            t('Atlanta')
          )
        ),
        t('india') => drupal_map_assoc(
          array(
            t('Maharashtra'),
            t('Gujarat'),
            t('Karnataka')
          )
        )
    );

    if (isset($options[$key])) {
        return $options[$key];
    }
    else {
        return array();
    }
}

?>

Problem was at line

$selected = isset($form_state['values']['field_country']) ? $form_state['values']['field_country'] : '';

you were passing the array to the selected value .i.e. countries array but the function ajax_get_state_options needs the key of selected country to return the array of states based on selected country.

I have tested this on php 5.3.27 Other suggestions :

  1. you can also use the existing field api wrapper generated by field api.

P.S. : Double slap myself to track this small mistake in 1h 30 minutes.

1

Have you tried one of the following modules?

Multiselect (this one suits the most)

Hierarchical Select (relatively easy to configure)

Conditional Fields (requires separate dropdown lists)

A combination of Multiselect and Hierarchical Select will mostly help you achieve what you have shown.

Regards,

Supriya Rajgopal

3
  • Can i use Hierarchical Select module with Form API to populate the Countries and States, using AJAX? Commented Nov 11, 2014 at 10:34
  • Yes shasi, I think you can use. Please read the README.txt in the module for more information. Commented Nov 12, 2014 at 6:04
  • Hierarchical select is broken in D7, maybe try Simple Hierarchical Select instead.
    – Darvanen
    Commented Nov 12, 2014 at 12:53
0

You return $form['field_states'] wrapped in <div id="states-wrapper"> to replace content of <div id="states-wrapper">. Try removing wrapper when returning form element via AJAX like:

function ajax_states_callback($form, $form_state) {
    unset($form['field_states']['#prefix']);
    unset($form['field_states']['#suffix']);
    return $form['field_states'];
}

You should consider moving $form['field_states'] to a container.

2
  • I tried removing wrapper while returning AJAX response, but it did not work. Can you clarify how to use a container for the field? Commented Nov 11, 2014 at 7:35
  • I meant, something like $form['field_states_container'] = array('#type' => 'container', '#prefix' => '<div id="states-wrapper">', '#suffix' => '</div>', 'field_states' => $form['field_states']); unset($form['field_states']);, however I'm not sure it won't break anything in further form processing, since it's not a custom field. Anyway you can try it to check whether it is the reason. Commented Nov 11, 2014 at 13:01
0

Try Reference Field Option Limit
Find the Documentation related to this module here Documentation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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