0

There are some fields (some required, some not) I need to (conditionally) hide in the user creation form:

function hook_form_alter(&$form, &$form_state, $form_id){
    switch($form_id) {
    case 'user_register_form':
        //variable_name is sent using hook_menu_output_alter()
        if ($_REQUEST['variable_name'] == 'variable_value') {
            $form['field_id'][LANGUAGE_NONE]['#required'] = FALSE;
            $form['field_id']['#access'] = FALSE;
        }
}

This generally works but not when I override a (required) entity reference field in the form to change its autocomplete path:

//in another module:
function hook_alter(&$form, &$form_state, $form_id){
    $form['entity_ref_field'][LANGUAGE_NONE][0]['target_id']['#autocomplete_path']= 'custom_autocomplete';
    $form['entity_ref_field'][LANGUAGE_NONE][0]['target_id']['#ajax'] = array(
            'callback' => 'custom_callback',
            'wrapper' => 'custom_wrapper',
            'method' => 'html',
            'effect' => 'fade',
    );
}

function hook_menu(){
    $items = array();
    $items['custom_autocomplete'] = array(
        'title' => '',
        'page callback' => 'custom_page_autocomplete',
        'access arguments' => array('access content'),
        'type' => MENU_CALLBACK,
    );
    return $items;
}

function custom_page_autocomplete($string) {
  $string = array(':s' => $string . '%');
    if ($string) {
        $results = db_query("SELECT title, nid FROM {node} as n WHERE type = 'custom_type' and title LIKE :s", $string);

        $items = array();

        foreach ($results as $result) {
            $items[$result->title.' ('.$result->nid.')'] = $result->title;
        }
        print drupal_json_encode($items);
    }
}

function custom_callback(&$form, &$form_state) {
    //if an entity reference field value is set, then find & assign values to some other fields, based on that entity:
    $entity_id = $form_state['values']['field_id']['und'][0]['target_id'];
    if ($selected_entity = node_load($entity_id)) {
        $form['field_somefield1'][LANGUAGE_NONE][0]['value']['#value'] = $selected_entity->field_someotherfield1['und'][0]['value'];
        $form['field_somefield2'][LANGUAGE_NONE][0]['value']['#value'] = $selected_entity->field_someotherfield2['und'][0]['value'];
        $form['field_somefield3'][LANGUAGE_NONE][0]['value']['#value'] = $selected_entity->field_someotherfield3['und'][0]['value'];
    }

    $commands = array();
    $commands[] = ajax_command_replace("#edit-field-user-somefield1", render($form['field_user_somefield1']));
    $commands[] = ajax_command_replace("#edit-field-user-somefield2", render($form['field_user_somefield2']));
    $commands[] = ajax_command_replace("#edit-field-user-somefield3", render($form['field_user_somefield3']));
    return array('#type' => 'ajax', '#commands' => $commands);
}

So, whenever an AJAX call is made by the overridden field and I then submit the form, all the (previously hidden) fields are visible again and the required fields become required again.

Any ideas why this is happening?

12
  • Is that specific variable in the URL still present for the AJAX submit? Perhaps your form alter isn't altering anything because the condition isn't met.
    – Andy
    Aug 15, 2014 at 11:04
  • The condition is not met for sure but I'm not sure how I can include the variable in the AJAX submit?
    – webmaniac
    Aug 15, 2014 at 11:10
  • Actually I think I was wrong on the first comment because it's not an AJAX form submission, it's just an AJAX request to the autocomplete handler. AFAIK that shouldn't involve any form processing (ie your alter shouldn't even be called IIUC). If you remove your customisations to the entityreference element, does the problem persist?
    – Andy
    Aug 15, 2014 at 12:01
  • No, the problem is not happening when the entity reference field is not overridden. Only when a call is made with my custom autocomplete path the problem is there.
    – webmaniac
    Aug 15, 2014 at 12:06
  • Could you add your autocomplete callback and the part of the form alter dealing with the autocomplete widget?
    – Andy
    Aug 15, 2014 at 12:08

1 Answer 1

0

The AJAX callback shouldn't alter the form (that should be handled by the form constructor or a form alter). This tends to be a good thing, because it forces you to put all your form logic in form functions, and that means they can degrade gracefully if JS isn't available. When dealing with AJAX forms, the callback typically just decides what part of the form to return. Take a look at the example from the examples module:

/**
 * Basic AJAX callback example.
 *
 * Simple form whose ajax-enabled 'changethis' member causes a text change
 * in the description of the 'replace_textfield' member.
 *
 * See @link http://drupal.org/node/262422 Form API Tutorial @endlink
 */
function ajax_example_simplest($form, &$form_state) {
  $form = array();
  $form['changethis'] = array(
    '#title' => t("Choose something and explain why"),
    '#type' => 'select',
    '#options' => array(
      'one' => 'one',
      'two' => 'two',
      'three' => 'three',
    ),
    '#ajax' => array(
      // #ajax has two required keys: callback and wrapper.
      // 'callback' is a function that will be called when this element changes.
      'callback' => 'ajax_example_simplest_callback',
      // 'wrapper' is the HTML id of the page element that will be replaced.
      'wrapper' => 'replace_textfield_div',
      // There are also several optional keys - see ajax_example_autocheckboxes
      // below for details on 'method', 'effect' and 'speed' and
      // ajax_example_dependent_dropdown for 'event'.
    ),
  );

  // This entire form element will be replaced whenever 'changethis' is updated.
  $form['replace_textfield'] = array(
    '#type' => 'textfield',
    '#title' => t("Why"),
    // The prefix/suffix provide the div that we're replacing, named by
    // #ajax['wrapper'] above.
    '#prefix' => '<div id="replace_textfield_div">',
    '#suffix' => '</div>',
  );

  // An AJAX request calls the form builder function for every change.
  // We can change how we build the form based on $form_state.
  if (!empty($form_state['values']['changethis'])) {
    $form['replace_textfield']['#description'] = t("Say why you chose '@value'", array('@value' => $form_state['values']['changethis']));
  }
  return $form;
}

/**
 * Callback for ajax_example_simplest.
 *
 * On an ajax submit, the form builder function is called again, then the $form
 * and $form_state are passed to this callback function so it can select which
 * portion of the form to send on to the client.
 *
 * @return array
 *   Renderable array (the textfield element)
 */
function ajax_example_simplest_callback($form, $form_state) {
  // The form has already been submitted and updated. We can return the replaced
  // item as it is.
  return $form['replace_textfield'];
}

Note how the dynamic change of the form happens in the form constructor while the callback is minimal.

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.