Hi attiks,

Extending Client side validation to build custom form validations

Provided below are code snippets to extend the validation of a custom for in a custom module to throw validation errors using modules Client side validation, Form API validation

Create custom module to extend fapi validation

hook_form in custom module
$form['field_ownrule'] = array(
                '#type' => 'textfield',
                '#title' => 'My Field4',
                '#required' => FALSE,
                '#rules' => array(
                  'numeric_own',
                  ),
                );
Rule Declaration in hook_fapi_validation_rules()
return array(
  'numeric_own' => array(
    'callback' => 'own_form_validation_rule_numeric',
    'error_msg' => t('You Use only numbers at %field.')
  ),
);
Rule Definition Function
function own_form_validation_rule_numeric($value){
if(is_numeric($value)){
  return TRUE;
}
else{
  return FALSE;
}
}

At this point the Rules get defined and the server side validations start working. The jquery validations not working as per the default one.

To extend the same, because we do not have any hook, copied function clientside_validation_fapi_clientside_validation_form_alter(), clientside_validation_fapi_after_build_recurse(), clientside_validation_fapi_regular() from clientside_validation_fapi.module.

And added code to my module as below,

//Function clientside_validation_fapi_clientside_validation_form_alter() copied from clientside_validation_fapi.module.
function imorph_form_clientside_validation_form_alter(&$form, &$form_state, &$js_rules) {
 imorph_form_fapi_after_build_recurse($form['#id'], $form, $form_state, $js_rules);
}

//Function copied from clientside_validation_fapi_after_build_recurse() from clientside_validation_fapi.module.
function imorph_form_fapi_after_build_recurse($form_id, &$form, &$form_state, &$js_rules){
 if ($children = array_values(element_children($form))) {
    foreach ($children as $index => $item) {
     $element = &$form[$item];
     $types = array(
       'textfield', 'textarea', 'select', 'radio', 'checkbox', 'password', 'file', 'radios', 'checkboxes',
     );
     if (isset($element['#type']) && in_array($element['#type'], $types)) {
       imorph_form_fapi_regular($form_id, $element, $js_rules);
     }
     clientside_validation_form_after_build_recurse($form_id, $element, $form_state, $js_rules);
    }
 }
}

//Function clientside_validation_fapi_regular() from clientside_validation_fapi.module.
function imorph_form_fapi_regular($form_id, $element, &$js_rules){
 if (isset($element['#name']) && isset($element['#rules'])) {
    $el_name = $element['#name'];
    $el_title = $el_name;
    if (isset($element['#title'])) {
     $el_title = $element['#title'];
    }
    $data = _fapi_validation_data('rules');
    foreach ($element['#rules'] as $rule) {
     $params = array($element['#value']);
     $error_message = NULL;

     // If $rule is an array, use error message if is set.
     if (is_array($rule)) {
       if (isset($rule['error'])) {
         $error_message = $rule['error'];
       }

       if (!isset($rule['rule'])) {
         drupal_set_message(t('Rule array with wrong structure on %field.', array('%field' => $element['#name'])), 'error');
         continue;
       }
       $rule = $rule['rule'];
     }

     preg_match('/^(.*?)(\[(.*)\])?$/', $rule, $rs);

     $rule = $rs[1];

     if (!isset($data[$rule])) {
       $error_message = t('Rule %rule not found!', array('%rule' => $rule));
       continue;
     }

     // Parsing parameters.
     if (isset($rs[3])) {
       $params[] = preg_split('/ *, */', $rs[3]);
     }
     $error = is_null($error_message) ? $data[$rule]['error_msg'] : $error_message;
     _imorph_form_set_fapi_validation($el_name, $el_title, $data[$rule]['callback'], $params, $js_rules, $error);
    }
 }
}

function _imorph_form_set_fapi_validation($name, $title, $callback, $params, &$js_rules, $message = ''){
 switch ($callback) {
    case 'imorph_form_validation_rule_numeric':
     _imoprh_form_set_number($name, $title, '.', $js_rules);
    break;
 }
}

function _imoprh_form_set_number($name, $title, $decimalpoint, &$js_rules){
 $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
 $js_rules[$name]['digits_negative'] = TRUE;
 $js_rules[$name]['messages']['digits_negative'] = t('Youuuu Use only numbers at !field.', array('!field' => $title));
}

@attiks, Could you validate the above approach. I am not confident as I am duplicate considerable amount of code. And also validate and tell me whether this won’t affect the default validation.

Thanks & Regards
Sarav.

Comments

attiks’s picture

Assigned: Unassigned » jelle_s

Related to #1280620: Webform Validation module, we need to provide hooks to handle the unknown types, for the moment we discard them.

attiks’s picture

Status: Active » Needs work

@Sarav, your code probably will work, we'll try to find something easier to use next week.

jelle_s’s picture

Version: 6.x-1.19 » 7.x-1.x-dev
Status: Needs work » Fixed

New hook (hook_clientside_validation_rule_alter) with documentation in the clientside_validation.api.php and clientside_validation.api.js files added to 6.x-1.x-dev and 7.x-1.x-dev. This should provide an easy way for developers to add Clientside Validation rules for their custom validation rules.

sarav.din33’s picture

Hi attiks & Jelle_S,

First of all lot's of thanks for you creation of new hook to extend our custom rule. It's very useful for me. I have one issue. Following is the code i wrote.

In hook_form

$form['field_4_ownrule'] = array(
                  '#type' => 'textfield',
                  '#title' => 'My Field4',
                  '#required' => TRUE,
                  '#rules' => array(
                    'numeric_own',
                    ),
                  );

  $form['field_5'] = array(
                  '#type' => 'textfield',
                  '#title' => 'My Field5',
                  '#required' => FALSE,
                  '#rules' => array(
                    'max_length_own',
                    ),
                  );

In hook_fapi_validation_rules()

function imorph_form_fapi_validation_rules() {
  return array(
    'numeric_own' => array(
      'callback' => 'imorph_form_validation_rule_numeric',
      'error_msg' => t('You Use only numbers at %field.')
    ),
    'max_length_own' => array(
      'callback' => 'imorph_form_validation_rule_max_length',
      'error_msg' => t('You Use only 10 numbers at %field.')
    ),
  );
}

In Rules definition

function imorph_form_validation_rule_numeric($value){
  if(is_numeric($value)){
    return TRUE;
  }
  else{
    return FALSE;
  }
}

function imorph_form_validation_rule_max_length($value){
  if(is_numeric($value)){
    return TRUE;
  }
  else{
    return FALSE;
  }
}

Implementation of hook_form_clientside_validation_rule_alter()

function imorph_form_clientside_validation_rule_alter(&$js_rules, $element, $context) {

  switch ($context['type']) {
    case 'fapi':
      if ($context['rule']['callback'] == 'imorph_form_validation_rule_max_length') {
        _imoprh_form_set_max_length($element['#name'], $element['#title'], $decimalpoint, &$js_rules);
      }
      if ($context['rule']['callback'] == 'imorph_form_validation_rule_numeric') {
        _imoprh_form_set_number($element['#name'], $element['#title'], $decimalpoint, &$js_rules);
      }
    break;

    default:

    break;
  }
}

Define Error message

function _imoprh_form_set_number($name, $title, $decimalpoint, &$js_rules){
  $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
  $js_rules[$name]['digits_negative'] = TRUE;
  $js_rules[$name]['messages']['digits_negative'] = t('Youuuuuuuuuuuu Use only numbers at !field.', array('!field' => $title));
}

function _imoprh_form_set_max_length($name, $title, $decimalpoint, &$js_rules){
  $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
  $js_rules[$name]['sarav'] = TRUE;
  $js_rules[$name]['messages']['sarav'] = t('You can not exceed 10 characters for !field.', array('!field' => $title));
}

I created two custom rules,
1. numeric_own
2. max_length_own

By using the Rule1, all the functionality is working fine. But in the Rule2 client side validation is not working.

Can you guide what is the mistake in the above code.

Thanks & Regards
Sarav.

jelle_s’s picture

Take a look at #1286870: example for defining extra validation rules. There's a full example available there. Everything in your code looks right to me, the only thing left to do (I think) is to add the javascript validation functions in your javascript file. (and ofcourse add the javascript file when needed).

mibfire’s picture

Jelle_S maybe u can change "code" to <?php in my php code to be highlighted?

jelle_s’s picture

@mibfire: Done, looks better indeed ;)

sarav.din33’s picture

Hi Jelle_S,

Lot's of thanks for your reply. I didn't put any java script function or file for any custom rule. In the above post i created two custom rule.

Rule 1: numeric_own
Rule 2: max_length_own

For both rule i didn't create any java script function or file.

Rule 1 working fine (both Client side and Server side validation), but Rule 2 not working in Server side only.

I didn't know how to add java script function or file for custom rule. Can you guide me how to add java script function for custom validation.

I saw @ http://drupal.org/node/1286870, but i didn't understand how java script it's works.

Thanks & Regards
Sarav.

jelle_s’s picture

The reason the first function works and the second doesn't is:

In the first function you do:

  $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
  $js_rules[$name]['digits_negative'] = TRUE;
  $js_rules[$name]['messages']['digits_negative'] = t('Youuuuuuuuuuuu Use only numbers at !field.', array('!field' => $title));

if you look in clientside_validation.js line 361 and following you'll see

jQuery.validator.addMethod("digits_negative", function(value, element, param) {
      return this.optional(element) || /^-?\d+$/.test(value);
    }, jQuery.format('Please enter only digits.'));

This means jquery.validator knows the "digits_negative" function.
But in the following function you do:

  $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
  $js_rules[$name]['sarav'] = TRUE;
  $js_rules[$name]['messages']['sarav'] = t('You can not exceed 10 characters for !field.', array('!field' => $title));

and jquery.validator does not know the "sarav" function.
So you have 2 options, a very simple one and a harder one:
1) (Simple)
Change

function _imoprh_form_set_max_length($name, $title, $decimalpoint, &$js_rules){
  $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
  $js_rules[$name]['sarav'] = TRUE;
  $js_rules[$name]['messages']['sarav'] = t('You can not exceed 10 characters for !field.', array('!field' => $title));
}

to

function _imoprh_form_set_max_length($name, $title, $decimalpoint, &$js_rules){
  $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
  $message = t('You can not exceed 10 characters for !field.', array('!field' => $title));
  _clientside_validation_set_minmaxlength($name, $title, '', 10, &$js_rules, $message);
}

You can do this because a function for maximum length also already exists for jquery.validator, and all you really want to do is change the message.

2) (Harder) You can add the method to jquery.validator:
in imporph_form.js:

//jQuery wrapper
(function ($) {
  //Define a Drupal behaviour with a custom name
  Drupal.behaviors.imorphFormBehavior = {
    attach: function (context) {
      //Add an eventlistener to the document reacting on the
      //'clientsideValidationAddCustomRules' event.
      $(document).bind('clientsideValidationAddCustomRules', function(event){
        //Add your custom method with the 'addMethod' function of jQuery.validator
        //http://docs.jquery.com/Plugins/Validation/Validator/addMethod#namemethodmessage
        jQuery.validator.addMethod("sarav", function(value, element, param) {
          return $.trim(value).length <= param;
          //Enter a default error message, numbers between {} will be replaced
          //with the matching value of that key in the param array, enter {0} if
          //param is a value and not an array.
        }, jQuery.format('Value can not exceed {0} characters'));
      });
    }
  }
})(jQuery);

In this case you have to change your php function as well:
Change

function _imoprh_form_set_max_length($name, $title, $decimalpoint, &$js_rules){
  $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
  $js_rules[$name]['sarav'] = TRUE;
  $js_rules[$name]['messages']['sarav'] = t('You can not exceed 10 characters for !field.', array('!field' => $title));
}

to

function _imoprh_form_set_max_length($name, $title, $decimalpoint, &$js_rules){
  //add your js file
  drupal_add_js(drupal_get_path('module', 'imporph_form') . '/imorph_form.js');
  $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
  $js_rules[$name]['sarav'] = 10; //change TRUE to 10
  $js_rules[$name]['messages']['sarav'] = t('You can not exceed 10 characters for !field.', array('!field' => $title));
}

both solutions should work. If you have any more questions, feel free to ask.

sarav.din33’s picture

Hi Jelle_S,

Thanks a lot for your immediate reply. I will try the above method.

Thanks & Regards
Sarav..

sarav.din33’s picture

Issue summary: View changes

Now i close the code tag

attiks’s picture

Assigned: jelle_s » Unassigned

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

sarav.din33’s picture

StatusFileSize
new42.79 KB
new35.8 KB
new41.98 KB

Hi attiks,

I need some help. I want do my custom required validation using Clientside validation form.

Form definition.

$form['rec_groups'] = array(
      '#title' => t('Communities'),
      '#type' => 'select',
      '#multiple' => 3,
      '#options' => $options,
      '#prefix' => '<div class="rec-groups" style="display:none">',
      '#suffix' => '</div>',
      '#rules' => array('select_comm'),
      );

I added a Select field with multiple values.

My Rule name is 'select_comm'.

Implementation of hook_fapi_validation_rules()

function hook_fapi_validation_rules() {
  return array(
    'select_comm' => array(
      'callback' => 'recommend_validation_check_communities',
      'error_msg' => t('%field not selected'),
    ),
  );
}

Definition of recommend_validation_check_communities()

function recommend_validation_check_communities($value) {
    if(empty($value)) {
      return FALSE;
    }
    else {
      return TRUE;
    }
}

Implementation of hook_clientside_validation_rule_alter()

function recommend_clientside_validation_rule_alter(&$js_rules, $element, $context) {
  switch ($context['type']) {
    case 'fapi':
      if ($context['rule']['callback'] == 'recommend_validation_check_communities') {
        _recommend_set_communities($element['#name'], $element['#title'], $decimalpoint, &$js_rules);
      }
      break;
  }
}

Definition of _recommend_set_communities()

function _recommend_set_communities($name, $title, $decimalpoint, &$js_rules) {
  drupal_add_js(drupal_get_path('module', 'assignment') . '/js/assignment_validation.js');
  $title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
  $js_rules[$name]['checkcommunity'] = TRUE;
  $js_rules[$name]['messages']['checkcommunity'] = t('Must Enter a value in !field.', array('!field' => $title));

}

I also define the error function in js

jQuery.validator.addMethod("checkcommunity", function(value, element, param) {
      if (!value) {
        return false;
      }
      else {
        return true;
      }
});

The above code didn't work. Because the form field type as Multiple select field.

When using the Multiple select field the js won't call. (Attachment1, 2)

When using Select field the js will call.(Attachment3)

Can you guide me how can i solve this problem.

Thanks & Regards
Sarav..

attiks’s picture

jelle_s’s picture

Component: Code » Documentation

Setting this to documentation for others to find

Edit: A documentation page was created for Clientside Validation.

arun ak’s picture

Hi Jelle_S,

I have tried the second method. It is working perfectly. Thank you verymuch!

Thanks and Regards
ARUN AK

arun ak’s picture

Issue summary: View changes

Updated issue summary.

padma28’s picture

Status: Closed (fixed) » Active

I have tried custom rule for a email field is already exists(FAPI + Clientside validation) validation as like in Clientside Validation Code Examples (https://drupal.org/node/1324772 ). It is working fine for onkeypress event using clientsideValidationAddCustomRules in the bind function. But i need to do this using onblur and onsubmit event. How can i change it?

Any help would be greatly appreciated, thanks!

arun ak’s picture

I think it will automatically work with those events also.

shyamala’s picture

@ARUN AK thanks! In continuation with Comment #17 What should we do it we want to restrict it to onblur & onsubmit alone and not onkeypress.

attiks’s picture

This is a setting, check admin/config/validation/clientside_validation/default or you can define it form each form separately

shyamala’s picture

Status: Active » Closed (fixed)

Pretty dumb question from my side! Thanks for the support @attiks