Sử dụng Drupal 7 Form API states system để tạo conditions between form elements

Sử dụng Drupal 7 Form API states system để tạo conditions between form elements

In this article we will look at how you can leverage the power of the Drupal 7 Form API states system to create dependencies between your form fields. In other words, when you programmatically write your forms, you’ll be able to make the behaviour of the elements depend on others.

To illustrate this power, we’ll write some code, but first, let’s see some theory.

What are the states and what do they do?

The states are simple properties or HTML attributes of DOM elements. When creating your form elements using the Form API, you can apply various states to them depending on the states of others. Thus there are 2 kinds of elements: those which act as the condition for others and those which depend on these conditions to change their state. I like the way Jeff Robbins of Lullabot categorises the elements by the possible states they can have: callers and listeners.

The possible states for the first group (the ones that trigger change in others)

  • empty
  • filled
  • checked
  • unchecked
  • expanded
  • collapsed
  • value

The possible states for the second group (the ones that get applied onto elements):

  • enabled
  • disabled
  • required
  • optional
  • visible
  • invisible
  • checked
  • unchecked
  • expanded
  • collapsed

And what does Drupal do? It provides all the necessary javascript to make all these behavioral alterations on the fly so you don’t have to worry about javascript or jQuery.

Example module

To illustrate how this all works, we are going to build a tiny module which declares a path to a form. Using this form, we will play around with some elements and see all this in action. So let’s dive in. You can download this small module if you want to follow along.

Assuming you have done the necessary to create your empty module, we can go ahead and declare the menu path using hook_menu():

/**
 * Implements hook_menu().
 */
 
function states_menu() {
 
  $items['states-example'] = array(
    'title' => 'Demonstrating the Form API states system',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('states_example_form'),
    'type' => MENU_NORMAL_ITEM,
    'access arguments' => array('access content'),
  );
 
  return $items;
}

This registers the states-example path which will render the states_example_form declared below:

function states_example_form($form, &$form_state) {
 
  $form = array();
 
  $form['name'] = array(
    '#title' => t('Your name'),
    '#type' => 'textfield',
  ); 
 
  $form['submit'] = array(
    '#type' => 'submit', 
    '#value' => t('Submit'),
  );
 
  return $form;
}

If you clear the cache and go to that path, you should see the form with 2 form elements: the name field and the submit button. No biggie. One thing to keep in mind for later is that the array key 'name' will be the actual DOM name of the form element.

Lets now add another element below the name field that will depend on the latter and show up only if the user enters something in the name field:

  $form['preferences'] = array(
    '#title' => t('Your preference'),
    '#type' => 'select',
    '#options' => array('moto' => 'Motorcycles', 'cars' => 'Cars'),
     // Show this field only if the name field is filled.
    '#states' => array(
      'visible' => array(
        'input[name="name"]' => array('filled' => TRUE),
      ),
    ),
  );

Lets scan through this array and see what’s what. Above the comment we can see that the element is a select list with 2 options and a title. Below the comment we declare that to this element will be applied the state visible when and if the DOM element of the type input with the name="name" (your classic css/jQuery selector and which happens to be the first element in our form) gets filled. Otherwise, the opposite state is applied - in this case, invisible.

So in the browser when the form is loaded on the page, only the name field is visible. When the user starts typing the name, the preferences field appears. Refresh the page and check it out. Drupal adds the necessary javascript for this to happen.

You can also do it the other way around. You can apply the state invisible when another element => array('filled' => FALSE). It will have the same effect except you declare it differently.

Next, let’s add another element that will depend on the preferences field:

  $form['brands'] = array(
    '#title' => t('Your favorite brand'),
    '#type' => 'select',
    '#options' => array('Toyota', 'BMW', 'Audi'),
     // The reverse of the previous field: hide this field if there is no name
    '#states' => array(
      'visible' => array(
        'select[name="preferences"]' => array('value' => 'cars'),
      ),
    ),
  );

This is another select list but the visibility of this element depends on the actual value of the previous one. So in the browser, this will only appear if and when the user selects that they prefer cars over motorcycles.

Let’s look at the next 2 elements:

  $form['marriage'] = array(
    '#title' => t('Are you married?'),
    '#type' => 'checkbox',
  );
 
  $form['spouse_preferences'] = array(
    '#title' => t('Your spouse\'s preference'),
    '#type' => 'radios',
    '#options' => array('moto' => 'Motorcycles', 'cars' => 'Cars'),
     // Show this field only if the user name is filled and if s/he is married.
    '#states' => array(
      'visible' => array(
        'input[name="name"]' => array('filled' => TRUE),
        'input[name="marriage"]' => array('checked' => TRUE),
      ),
    ),
  );

The first one is a simple checkbox that will show no matter what. But the second one is another select list and here we can see that the visibility of this depends on 2 previous elements. So this will show only when and if the user fills in a name and checks the box confirming that s/he is married. A so called AND condition, if you will. I will tell you write off the bat that you cannot do OR conditions with the states system - at least not that I could figure out. If you manage to find a way, let me know.

And the last two elements we will look at in this article:

  $form['kids'] = array(
    '#title' => t('How many kids do you have?'),
    '#type' => 'select',
    '#options' => array(0, 1, 2),
  );
 
  $form['kids_preferences'] = array(
    '#title' => t('Your kids\' preference'),
    '#type' => 'radios',
    '#options' => array('moto' => 'Motorcycles', 'cars' => 'Cars'),
     // Show this field only if the user has kids.
    '#states' => array(
      'visible' => array(
        'input[name="marriage"]' => array('checked' => TRUE),
      ),
      'disabled' => array(
        'select[name="kids"]' => array('value' => '0'),
      ),
    ),
  );

Again the first one will show no matter what, but this time it's a select list. The second one depends on two other elements for two different state changes. First, it will become visible only if the the user checks the box that s/he is married. Second, it will become disabled if the user selects that s/he has 0 kids. Neat.

Conclusion

As i mentioned in the beginning of the article, there are a number of different states available for elements and Drupal handles all the javascript for you. We cannot go over all of them here nor try all the different combinations of what you can do with the states system. I thus encourage you experiment further on your own. You can even download this little module and use it as a starting point.

And as always, Drupal.org is your friend and you can find some more information on the states system at the following pages:

Bạn thấy bài viết này như thế nào?: 
No votes yet
Ảnh của Khanh Hoang

Khanh Hoang - Kenn

Kenn is a user experience designer and front end developer who enjoys creating beautiful and usable web and mobile experiences.

Tìm kiếm bất động sản

 

Advertisement

 

jobsora

Dich vu khu trung tphcm

Dich vu diet chuot tphcm

Dich vu diet con trung

Quảng Cáo Bài Viết

 
Khái niệm mới Decoupling Drupal (decoupled CMS)

Khái niệm mới Decoupling Drupal (decoupled CMS)

Drupal 8 will make huge strides in this area, but alas it's not out yet. Fortunately the answer to the second problem is the first; it is entirely possible to build a solid, scalable, performant RESTful web service with Drupal 7

Nhanh tay “thử ngay” giao diện mới của Youtube!

Nhanh tay “thử ngay” giao diện mới của Youtube!

Youtube đang thử nghiệm một thiết kế trang chủ mới, thông tin này đang lan truyền trên các mạng xã hội Facebook, Google +, Twitter.

Triển lãm Palme Việt Nam về thiết bị nghe - nhìn, âm thanh, ánh sáng...

Triển lãm Quốc tế lần thứ 3 tại Việt Nam về Thiết bị Nghe – Nhìn, Âm thanh – Ánh sáng chuyên nghiệp, Truyền hình, Tích hợp hệ thống và Công nghệ giải trí sẽ trở lại Thành phố Hồ Chí Minh sau một năm vắng bóng.

Công ty diệt chuột T&C

 

Diet con trung