20

What is the easiest way to get some javascript magic for enabling/disabling form fields based on another field's value? This sounds like something there should be an helper for somewhere, but I can't find it. I am looking for a solution that is not restricted to nodes.

  • Not sure this is really a Drupal question. It's a JavaScript one and should be asked on Stack Overflow. – Camsoft Mar 8 '11 at 9:44
  • 3
    I was looking for a Drupal solution. I am capable of writing the Javascript for it, but it really feels like it should be plugged into the Form API somehow. – Fuzzy76 Mar 8 '11 at 9:55
  • That's cool. I don't think that was clear in the original question. I'm not aware any module that could do this. – Camsoft Mar 8 '11 at 9:59
18

The magic is using the #ahah/#ajax property with form elements, that way you can define what should trigger the modification and what should be modified as a result, plus it integrates seamlessly with jQuery.

This is the important part from the example below:

'#ajax' => array(
    'event' => 'change',
    'callback' => 'myajax_ajax_callback',
    'wrapper' => 'dropdown_second_replace',
),

Here is an example that shows a form-based page with two dropdowns: the list of options in the second dropdown depend on the selection in the first dropdown.

<?php

/**
 * Implementation of hook_menu().
 * Registers a form-based page that you can access at "http://localhost/myajax"
 */
function myajax_menu(){
    return array(
        'myajax' => array(
            'title' => 'A page to test ajax',
            'page callback' => 'drupal_get_form',
            'page arguments' => array('myajax_page'),
            'access arguments' => array('access content'), 
        )
    );
}



/**
 * A form with a dropdown whose options are dependent on a
 * choice made in a previous dropdown.
 *
 * On changing the first dropdown, the options in the second are updated.
 */
function myajax_page($form, &$form_state) {
    // Get the list of options to populate the first dropdown.
    $options_first = myajax_first_dropdown_options();

    // If we have a value for the first dropdown from $form_state['values'] we use
    // this both as the default value for the first dropdown and also as a
    // parameter to pass to the function that retrieves the options for the
    // second dropdown.
    $value_dropdown_first = isset($form_state['values']['dropdown_first']) ? $form_state['values']['dropdown_first'] : key($options_first);

    $form['dropdown_first'] = array(
        '#type' => 'select',
        '#title' => 'First Dropdown',
        '#options' => $options_first,
        '#default_value' => $value_dropdown_first,

        // Bind an ajax callback to the change event (which is the default for the
        // select form type) of the first dropdown. It will replace the second
        // dropdown when rebuilt
        '#ajax' => array(
            // When 'event' occurs, Drupal will perform an ajax request in the
            // background. Usually the default value is sufficient (eg. change for
            // select elements), but valid values include any jQuery event,
            // most notably 'mousedown', 'blur', and 'submit'.
            'event' => 'change',
            'callback' => 'myajax_ajax_callback',
            'wrapper' => 'dropdown_second_replace',
        ),
    );
    $form['dropdown_second'] = array(
        '#type' => 'select',
        '#title' => 'Second Dropdown',
        // The entire enclosing div created here gets replaced when dropdown_first
        // is changed.
        '#prefix' => '<div id="dropdown_second_replace">',
        '#suffix' => '</div>',
        // when the form is rebuilt during ajax processing, the $value_dropdown_first variable
        // will now have the new value and so the options will change
        '#options' => myajax_second_dropdown_options($value_dropdown_first),
        '#default_value' => isset($form_state['values']['dropdown_second']) ? $form_state['values']['dropdown_second'] : '',
    );
    return $form;
}

/**
 * Selects just the second dropdown to be returned for re-rendering
 *
 * 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 renderable array (the second dropdown)
 */
function myajax_ajax_callback($form, $form_state) {
    return $form['dropdown_second'];
}


/**
 * Helper function to populate the first dropdown. This would normally be
 * pulling data from the database.
 *
 * @return array of options
 */
function myajax_first_dropdown_options() {
    return array(
        'colors' => 'Names of colors',
        'cities' => 'Names of cities',
        'animals' => 'Names of animals',
    );
}


/**
 * Helper function to populate the second dropdown. This would normally be
 * pulling data from the database.
 *
 * @param key. This will determine which set of options is returned.
 *
 * @return array of options
 */
function myajax_second_dropdown_options($key = '') {
    $options = array(
        'colors' => array(
            'red' => 'Red',
            'green' => 'Green',
            'blue' => 'Blue'
        ),
        'cities' => array(
            'paris' => 'Paris, France',
            'tokyo' => 'Tokyo, Japan',
            'newyork' => 'New York, US'
        ),
        'animals' => array(
            'dog' => 'Dog',
            'cat' => 'Cat',
            'bird' => 'Bird'
        ),  
    );
    if (isset($options[$key])) {
        return $options[$key];
    }
    else {
        return array();
    }
}
  • This is the right way to alter a form depending on the value of one of its fields. But to hide/show or enable/disable fields, the #states property on form element is easier. – Pierre Buyle Aug 1 '11 at 18:24
6

Doesn't the Conditional Fields module do just that?

When editing a node, the controlled fields are dynamically shown and hidden with JavaScript.

  • For node forms and CCK fields, yes. But I wanted something that could be used in other circumstances. I'll clarify my question. – Fuzzy76 Mar 8 '11 at 9:53
3

There are two different systems which you can use:

  • #ahah/#ajax allows you to submit the form with AJAX and rebuild it on the server side. Useful when you actually want to add new form elements, typical example in D6 is upload.module. Already explained above.
  • New in Drupal 7 is the #states system which allows you to do stuff like showing/hide/enable/disable form elements based on other elements. See http://www.randyfay.com/node/58 for more information about that.
1

The easiest method would be to write your own JavaScript and use jQuery to attach event handlers to the blur and focus events. Then when your callbacks are fired, disable / enable the fields based on your logic.

  • And if he's not capable of writing his own jQuery? Would not a Drupal module be easy than coding? - Due to clarification of the question, I retract my comment. – Decipher Mar 8 '11 at 9:53
  • Firstly I was not aware of the Conditional Fields module myself, secondly how much overhead will a module like this add to his project over some simple client-side JS? – Camsoft Mar 8 '11 at 9:56

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.