0

I'm working on a D7 custom form with AJAX dependent dropdown, and I can't figure how to update two fields based on a selection from the first field. Here's the code so far:

/**
 * Implements hook_form().
 *
 * Form builder function for export form
 */
function mymodule_form($node, &$form_state) {
  $form = array();

  // Get list of available entity types.
  $entity_options = array('commerce_product' => t('Product'), 'node' => t('Node'));
  $selected_entity_type = isset($form_state['values']['entity_type']) ? $form_state['values']['entity_type'] : key($entity_options);

  $form['entity_type'] = array(
    '#type' => 'select',
    '#title' => t('Entity Type'),
    '#description' => t('Select the desired entity type'),
    '#options' => $entity_options,
    '#default_value' => $selected_entity_type,
    '#ajax' => array(
      'callback' => 'mymodule_dependent_dropdown_callback',
      'wrapper' => 'bundles-replace'
    ),
  );

  if ($selected_entity_type == 'commerce_product') {
    $bundle_options['product'] = t('Product');
  }
  else {
    // Get available node types.
    $node_types = node_type_get_types();
    $bundle_options = array();

    // Create an array to populate the node options.
    foreach($node_types as $type) {
      $bundle_options[$type->orig_type] = $type->name;
    }
  }

  $bundle_options = _mymodule_get_bundles($selected_entity_type);
  // Get the $selected_bundle based on the entity field. Since there are multiple dependent fields in
  // this form,  we can't really depend on $form_state['values'].
  $selected_bundle = isset($form_state['triggering_element']) && $form_state['triggering_element']['#name'] == 'bundle'? $form_state['triggering_element']['#value'] : key($bundle_options);

  $form['bundle'] = array(
    '#type' => 'select',
    '#title' => t('Bundle'),
    '#description' =>t('Select the content type to export'),
    '#options' => $bundle_options,
    '#default_value' => $selected_bundle,
    '#ajax' => array(
      'callback' => 'mymodule_dependent_dropdown_callback',
      'wrapper' => 'fields-replace'
    ),
    '#prefix' => '<div id="bundles-replace">',
    '#suffix' => '</div>',
  );

  $field_options = _mymodule_get_fields($selected_entity_type, $selected_bundle);

  $form['fields'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Fields'),
    '#prefix' => '<div id="fields-replace">',
    '#suffix' => '</div>',
    '#options' => $field_options
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Export'),
  );

  return $form;
}

/**
 * Since the controlling logic for populating the form is in the form builder
 * function, all we do here is select the element and return it to be updated.
 *
 * @return array
 *   Renderable array (the second dropdown)
 */
function mymodule_dependent_dropdown_callback($form, $form_state) {
  // The returned field depends on the triggering form field.
  switch ($form_state['triggering_element']['#name']) {
    case 'bundle':
      return $form['fields'];
      break;
    case 'entity_type':
      return $form['bundle'];
      break;
  }
}

/**
 * Helper function to populate the fields dropdown.
 *
 * @param string $key
 *   This will determine which set of options is returned.
 *
 * @return array
 *   Dropdown options
 */
function _mymodule($entity_type, $bundle) {
  // Get fields for the selected node type.
  $fields = field_info_instances($entity_type, $bundle);
  $return = array();

  foreach ($fields as $field) {
    $return[$field['field_name']] = $field['label'];
  }

  if (count($return) == 0) {
    $return['none'] = t('No fields for content type');
  }

  return $return;
}

/**
 * Helper function to populate the node_type dropdown.
 *
 * @param string $key
 *   This will determine which set of options is returned.
 *
 * @return array
 *   Dropdown options
 */
function _mymodule_get_bundles($key = '') {
  $return = array();
  if($key == 'commerce_product') {
    $return['product'] = t('Product');
  }
  else {
    $entities = entity_get_info();
    $bundles = $entities[$key]['bundles'];

    foreach ($bundles as $bundle_key => $bundle) {
      $return[$bundle_key] = $bundle['label'];
    }
  }

  return $return;
}

So what I want is to update both the bundle and fields fields when the entity_type field is changed. As it is now, the bundles field is updated when entity_type is changed, and fields is changed correctly when bundles is changed, but I can't get fields to update when entity_types is changed. When the code gets to where the fields field is built in the form builder function, the #options value has the correct values, but the page isn't updated with them. To try to accomplish that, I tried this code based on this answer:

function mymodule_dependent_dropdown_callback($form, $form_state) {
  // The returned field depends on the triggering form field.
  switch ($form_state['triggering_element']['#name']) {
    case 'bundle':
      return $form['fields'];
      break;
    case 'entity_type':
      return array(
        '#type' => 'ajax',
        '#commands' => array(
          ajax_command_replace("#bundles-replace", render($form['bundles'])),
          ajax_command_replace("#fields-replace", render($form['fields']))
        )
      );
      break;
  }
}

but the end result is that the fields field completely disappears from the page, even though $form['fields'] exists at that point in mymodule_dependent_dropdown_callback().

Based on this answer, I also tried replacing the entire form, like so:

function mymodule_form($node, &$form_state) {
  $form = array();

  $form['#prefix'] = '<div id="content-export-form">';
  $form['#suffix'] = '</div>';

  $form['entity_type'] = array(
    '#type' => 'select',
    '#title' => t('Entity Type'),
    '#description' => t('Select the desired entity type'),
    '#options' => $entity_options,
    '#default_value' => $selected_entity_type,
    '#ajax' => array(
      'callback' => 'mymodule_dependent_dropdown_callback',
      'wrapper' => 'content-export-formt'
    ),
  );
       ...
}

function mymodule_dependent_dropdown_callback($form, $form_state) {
  return $form;
}

but that didn't replace either 'bundleor 'fields when entity_type was updated.

What do I have to change to get the bundles and fields fields to be updated on the page when entity_type is changed?

Thanks.

UPDATE . After looking through my code again, I realized I had a typo in the wrapper definition on the entity field:

'wrapper' => 'content-export-formt'

Once I fixed that, it worked, sort of. When I first change the entity_type field to Node, it changes the bundle field values appropriately and sets the options for the fields field appropriately. However, when I select a different value in fields, instead of replacing the form fields, it adds a new instance of the entity_type and bundle fields on the page, and this repeats for each time I select a new value from the bundle field.

0

In order to not be like this guy, here's how I got it working.

It turns out I was pretty close. The only thing I really had left to do was change the #wrapper setting for the bundle field to be content-export-form as well.. Once I did that, the form worked like I wanted. Here's the final form builder function:

function mymodule_form($node, &$form_state) {
  $form = array();

  $form['#prefix'] = '<div id="content-export-form">';
  $form['#suffix'] = '</div>';

  // Get list of available entity types.
  $entity_options = array('commerce_product' => t('Product'), 'node' => t('Node'));
  $selected_entity_type = isset($form_state['values']['entity_type']) ? $form_state['values']['entity_type'] : key($entity_options);

  $form['entity_type'] = array(
    '#type' => 'select',
    '#title' => t('Entity Type'),
    '#description' => t('Select the desired entity type'),
    '#options' => $entity_options,
    '#default_value' => $selected_entity_type,
    '#ajax' => array(
      'callback' => 'mymodule_dependent_dropdown_callback',
      'wrapper' => 'content-export-form'
    ),
  );

  if ($selected_entity_type == 'commerce_product') {
    $bundle_options['product'] = t('Product');
  }
  else {
    // Get available node types.
    $node_types = node_type_get_types();
    $bundle_options = array();

    // Create an array to populate the node options.
    foreach($node_types as $type) {
      $bundle_options[$type->orig_type] = $type->name;
    }
  }

  $bundle_options = _mymodule_get_bundles($selected_entity_type);
  // Get the $selected_bundle based on the entity field. Since there are multiple dependent fields in
  // this form,  we can't really depend on $form_state['values'].
  $selected_bundle = isset($form_state['triggering_element']) && $form_state['triggering_element']['#name'] == 'bundle'? $form_state['triggering_element']['#value'] : key($bundle_options);

  $form['bundle'] = array(
    '#type' => 'select',
    '#title' => t('Content Type'),
    '#description' =>t('Select the content type to export'),
    '#options' => $bundle_options,
    '#default_value' => $selected_bundle,
    '#ajax' => array(
      'callback' => 'mymodule_dependent_dropdown_callback',
      'wrapper' => 'content-export-form'
    ),
  );

  $field_options = _mymodule_get_fields($selected_entity_type, $selected_bundle);

  $form['fields'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Fields'),
    '#options' => $field_options
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Export'),
  );

  return $form;
}

and here's the ajax callback function:

function mymodule_dependent_dropdown_callback($form, $form_state) {
  // We're returning the whole form since multiple fields are being updated.
  return $form;
}

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.