Skip to main content

How to add AJAX validation to a specific field in node form

Every project is unique and has something new to learn. This fact was realized again in my recent work. As the title says, the task was as simple as adding AJAX validation to a specific field (not for the entire form). While this looked pretty easy at first glance but had to perceive and apply a little crux of Form and Field API.


The field in which I had to execute this task comes from Link module. Essentially I need to check for duplicate URL. If the URL already exists in other node, I need to show error message in onblue event. Like most other CCK field module, it does implement its own Form API #type link_field  that can take URL and title.


My first endeavour was to implement a hook_form_alter() in a custom module as to inject #ajax property to $form['field_link']. But this trick didn't work as expected. Reason, the #ajax property does not work with custom type provided by Link module. The next essay was to try the same trick but in #after_build function. Again it turned out a futile attempt. After spending a couple of hours I realized  that  #after_build was too late to inject #ajax property. 


Later, I learned from Tushar  (founder @unfollowersme) the #process property where custom form API types are expanded to primitive types. In my case link_field to textfield. Adding #ajax to expanded url textfield field did the trick.


The minimal version of code that worked for me is below,

  1. /**
  2.  * Implements hook_form_alter()
  3.  */
  4. function kf_link_form_alter(&$form, &$form_state, $form_id) {
  5.   if ('example_node_form' == $form_id) {
  6.     //$form['field_link'][$language]['#after_build'][] = '_kf_link_field_link_after_build';
  7.     $form['field_link'][$language][0]['#process'] = array('link_field_process', '_kf_link_field_link_process');
  8.   }
  9. }
  11. /**
  12.  * Custom form api process function
  13.  */
  14. function _kf_link_field_link_process($element, &$form_state, $form) {
  15.   $element['url']['#description'] = '<div id="example-link"></div>';
  16.   $element['url']['#ajax'] = array(
  17.     'callback' => 'kf_link_ajax_callback',
  18.     'wrapper' => 'example-link',
  19.   );
  20.   return $element;
  21. }
  23. /**
  24.  * Custom ajax callback function
  25.  */
  26. function kf_link_ajax_callback(&$form, $form_state) {
  27.   $values = $form_state['values'];
  28.   $field_link = $values['field_link'];
  29.   $language = $values['language'];
  30.   $url = $field_link[$language][0]['url'];
  31.   $duplicate_nodes = _kf_link_get_url_nid($url);
  32.   foreach ($duplicate_nodes as $duplicate_node) {
  33.     if (isset($duplicate_node->nid) && ($duplicate_node->nid != $values['nid'])) {
  34.       drupal_set_message(t('This URL already exists in <a href="!url">!title</a>', array('!title' => $duplicate_node->title, '!url' => "node/{$duplicate_node->nid}")), 'error');
  35.     }
  36.   }
  37.   $commands = array();
  38.   $commands[] = ajax_command_html(NULL, theme('status_messages'));
  39.   return array(
  40.     '#type' => 'ajax',
  41.     '#commands' => $commands,
  42.   );
  43. }



Supriya Rajgopal (not verified)

Wed, 02/03/2016 - 07:30

Hi Dake,

I think the form won't submit if you use 'form_set_error()' instead of 'drupal_set_message()'.

Supriya Rajgopal