Telerik blogs

Form validation is like politics: It sounds like a simple problem to solve. It's not until you get involved that you discover what a nasty and convoluted business it is.

Kendo UI can't solve all of the issues of politics, but it does have quite a few validation tricks up it's sleeve. Some widgets (like the Kendo UI Grid) have validation baked right in. The same framework that these widgets use is exposed for you to use as well.

The Kendo UI Validator provides wide surface coverage for most common validation needs. It's also got a dead simple API that you can use to build your own validation rules based on your business logic. In this article, we'll look at how you can define custom rules in the Kendo UI validator to do simple tasks (like field comparison), all the way up to more advanced and fancy validation like AJAX calls to validate username availability in the database.

Setting Up Validation

The form validator is rather easy to setup. First though, we'll be needing a simple form. In this example, we'll be registering users with a back-end system asking for their Username, Password and Email Address. Here are the rules for the form...

  1. All fields are required
  2. The password must be confirmed
  3. The email address must be valid
  4. The username must be available in the backend system

We will use the Kendo UI Validator to ensure that all of these rules are met before the browser is allowed to submit the form. Here is our sample form:

Simple Form

<!DOCTYPE html>
<html>

  <head>
  <title>
    Dashboard :: Register
  </title>
  <link rel="stylesheet" href="//cdn.kendostatic.com/2013.3.1119/styles/kendo.common.min.css">
  <link rel="stylesheet" href="//cdn.kendostatic.com/2013.3.1119/styles/kendo.bootstrap.min.css">
  <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
  <link rel="stylesheet" href="stylesheets/style.css">
  </head>

  <body>
    <div class="login well">
      <h1>Welcome aboard!</h1>
      <hr>
      <form id="register-form" method="POST" action="/register">
        <div class="form-group">
          <label for="username">Username</label>
          <input type="text" name="username" 
                 id="username" placeholder="Enter username" 
                 class="form-control">
        </div>
        <div class="form-group">
          <label for="password">Password</label>
          <input type="password" name="password" 
                 id="password" placdeholder="Enter Password" 
                 class="form-control">
        </div>
        <div class="form-group">
          <label for="confirm-password">Confirm Password</label>
          <input type="password" name="confirm-password" 
                 id="confirm-password" placeholder="Confirm password" class="form-control">
        </div>
        <div class="form-group">
          <label for="email">Email</label>
          <input type="email" name="email" 
                 id="email" placeholder="Enter email" class="form-control">
        </div>
        <div class="form-group">
          <button class="btn btn-default">Register</button>
          <a href="login">Or Login</a>
        </div>
      </form>
    </div>

    <script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="//cdn.kendostatic.com/2013.3.1119/js/kendo.all.min.js"></script>

  </body>
</html>

I've already included Kendo UI in that form, so we can tell Kendo UI to validate this form by selecting the <form> element with jQuery and calling the kendoValidate method on it.

Apply Kendo UI Validation To The Form

// goes right before the closing body tag..

  <script>
    $("#register-form").kendoValidator();
  </script>

  </body>
</html>

It's important that you select the <form> element when using the validator, and not a higher level node as you will get unexpected behavior if you do.

Now Kendo UI won't allow the form to be submitted unless all validation rules have been satisfied. We haven't added any yet, so lets get to it.

The Kendo UI Validator follows the HTML5 Forms spec as much as it can. This means that to make a field required, we simply need to include the required attribute on the input we want to check. The HTML5 Forms spec does not offer a way to customize messages for required fields. As such, Kendo UI falls back to data attributes. You can use the data-required-msg to specify a custom message.

<form id="register-form" method="POST" action="/register">

  <div class="form-group">
    <label for="username">Username</label>

    <input type="text" name="username" 
           id="username" placeholder="Enter username" 
           class="form-control"
           required data-required-msg="Username is a required field">
  </div>

  <div class="form-group">
    <label for="password">Password</label>

    <input type="password" name="password" 
           id="password" placdeholder="Enter Password" 
           class="form-control"
           required data-required-msg="Password is required">
  </div>

  <div class="form-group">
    <label for="confirm-password">Confirm Password</label>

    <input type="password" name="confirm-password" 
           id="confirm-password" placeholder="Confirm password" class="form-control"
           required data-required-msg="You must confirm your password">
  </div>

  <div class="form-group">
    <label for="email">Email</label>

    <input type="email" name="email" 
           id="email" placeholder="Enter email" class="form-control"
           required data-required-msg="Email is required">
  </div>

  <div class="form-group">
    <button class="btn btn-default">Register</button>
    <a href="login">Or Login</a>
  </div>
</form>

Now all of the form fields have to be populated in order for the form to be submitted.

If you filled in some of the fields above, you might have noticed that the Kendo UI Validator is actually checking the email address to make sure it's in the right format. That's odd because we never actually told it we wanted to do that.

Actually, we did.

Like the HTML5 Forms specification, the Kendo UI Validator will validate the field value based on it's type. By specifying an input of type email, the Kendo UI Validator goes ahead and checks to make sure that the value is in fact an email address. No frustrating Regex required on our part!

Customizing Messages For Built-In Validation

The email address validation is built-in and so is it's message. We can override these messages by specifying them in the messages object of the Kendo UI Validator.

Specifying Custom Messages

<script>
  $("#register-form").kendoValidator({
    messages: {
      email: "That does not appear to be a valid email address"
    }
  });
</script>

Custom Validation Rules

That takes care of all of the simple validation. Out of the box, the Kendo UI Validator will validate email addresses, dates, whether one date is greater than another, url's, steps, min and max constraints, required fields and custom regex patterns. That's quite a bit of validation logic that you don't have to worry about. However, it's highly likely that you will have some squirly business validation for your forms that you will need to implement yourself. Let's take a look at how we can implement a simple rule that Kendo UI does not provide out of the box.

Comparison Validation

The "password" and "confirm password" and fields need to match. Kendo UI does not provide a "matching" rule out of the box, so we are going to create it ourselves. This rule will check two different fields to see if their trimmed string values match.

Create A Custom Rule

$("#register-form").kendoValidator({
  rules: {
    matches: function(input) {
      // custom matching logic goes here
      return true;
    }
  }
  messages: {
    email: "That does not appear to be a valid email address"
  }
});

Kendo UI Validation rules run on every input in the form, passing the input into the matches function. These functions should ALWAYS return true for any inputs we don't want to validate.

Since we need to validate that the "password" field matches the "confirm password" field, we could just check to see if the input is one of those two and then match them against each other. However, we can create a generic matching rule that we could use again on this form by following Kendo UI's lead and using some custom data attributes.

<div class="form-group">
  <label for="password">Password</label>
  <input type="password" name="password" 
         id="password" placeholder="Enter Password" class="form-control"
         required data-required-msg="Password is required">
</div>
<div class="form-group">
  <label for="confirm-password">Confirm Password</label>
  <input type="password" name="confirm-password" 
         id="confirm-password" placeholder="Confirm password" class="form-control"
         required data-required-msg="You must confirm your password"
         data-matches="#password" data-matches-msg="The passwords do not match">
</div>

The rule just needs to check for the presence of the data-matches attribute on the input. If the field exists, use it's value to select the input it needs to match. Then we just check the trimmed values against each other and return true if the field is valid or false if it's not.

The message that is returned can also be a function instead of just a string. This means that we can specify the exact message we want to use using the data-matches-msg attribute.

$("#register-form").kendoValidator({
  rules: {
    matches: function(input) {

      var matches = input.data('matches');

      // if the `data-matches attribute was found`
      if (matches) {

        // get the input to match
        var match = $(matches);

        // trim the values and check them
        if ( $.trim(input.val()) === $.trim(match.val()) )  {

          // the fields match
          return true;

        } else {

          // the fields don't match - validation fails
          return false;

        }
      }

      // don't perform any match validation on the input
      return true;

    }
  },
  messages: {
    email: "That does not appear to be a valid email address",
    matches: function(input) {
      return input.data("matchesMsg");
    }
  }
});

Notice that the data attributes that are dash separated become camel cased when retrieved using jQuery

Now we have created a generic matching rule that we could apply to any set of inputs that need to match. All of the configuration is done with HTML, so there is no need to duplicate JavaScript code and create more than one matching rule. In this case, I applied it to just the "confirm password" field.

Following this pattern, we can extend the validator to specify all of our custom rules. We don't have to stop with the simple though. Let's look at how to do some rather complex validation using this same strategy and a bit of creative thinking.

Advanced Validation

Sometimes, you have a rule that only the server can validate. A common scenario is to have a constraint in the database that can only be validated by checking the input value against the database. For instance, the "Users" table behind this registration form probably requires that all users have a unique username.

One way to handle this is to just let the user submit the form. However, a better user experience would be if we checked for them in real time and let them know if the username is available or not before we ever allow the form to be submitted.

Handling Validation In Stages

This sort of validation is complex not just because it requires a server method and an AJAX call, but because that AJAX call is asynchronous and that adds a level of complexity. By default, the Kendo UI Validator fires any validation rules when the field blurs (loses focus). This means that when the user exits the username field, we enter the following decision tree...

The first thing we need to do is add in the HTML data attributes that we need to create this available rule for username.

Add data-available Attributes

<div class="form-group">
  <label for="username">
    Username
  </label>
  <input type="text" name="username" id="username" placeholder="Enter username"
  required data-required-msg="Username is required" class="form-control"
  data-available data-available-url="validate/username" 
  data-available-msg="The username #: data # is not available">
</div>

The data-available attribute flags this field for the available validation rule (which hasn't been created yet). The data-available-url lets us specify which URL we want to use to check the value, and the message specifies the feedback we give to the user. In this case it's in the form of a Kendo UI Template, which will give the user very specific feedback.

Creating The Rule

Now it's time to create the rule just like we did for matches. This rule is going to be complex, so lets handle it in stages.

Entering Into A "Checking" State

The first thing that we need to do is simply to check and see if the field is flagged via the data-available attribute. If it is, we need to make an AJAX call to the server to validate the field. Since the AJAX call is asynchronous, we need to just return false for now and set the message to "checking...".

Add The 'Available' Validation Rule

$("#register-form").kendoValidator({
  rules: {
    matches: function(input) {

      var matches = input.data('matches');

      // if the `data-matches attribute was found`
      if (matches) {

        // get the input to match
        var match = $(matches);

        // trim the values and check them
        if ( $.trim(input.val()) === $.trim(match.val()) )  {

          // the fields match
          return true;

        } else {

          // the fields don't match - validation fails
          return false;

        }
      }

      // don't perform any match validation on the input
      return true;

    },

    available: function(input) {

      var validate = input.data('available');

      if (typeof validate !== 'undefined' && validate !== false) {

        var url = input.data('availableURL');

        $.ajax({
          url: settings.url,
          dataType: 'json',
          data: { value: element.val() },

          success: function(data) {

            // data object contains true or false on whether
            // or not the field is valid

          }
        });

        // the ajax call is asynchronous, so just return false for now
        return false;

      }

      // this rule does not apply to this field
      return true;
  },
  messages: {
    email: "That does not appear to be a valid email address",
    matches: function(input) {
      return input.data("matchesMsg");
    },
    available: function(input) {
      return "checking..."
    }
  }
});

This will kick off the validation when the field loses focus, execute the AJAX call and return false. This is because the AJAX call is asynchronous and JavaScript is not going to wait for it to come back. Returning false and setting the message to 'checking...' will prevent the form from being submitted, while giving the user feedback that something is going on.

We are now in the "checking" state, and the field is technically invalid.

Handling The AJAX Return

The next step is to handle the AJAX call when it comes back. When the field goes into 'checking...' state, it's really marked as invalid. We need to mark it as valid or invalid (depending on the response from the server) and then tell Kendo UI to validate it again so we can remove the "checking..." invalid flag if necessary. If the field is in fact invalid, then we need to swap the 'checking...' message out with a helpful message that tells the user what happened.

In order to do this, we need to keep a cached state for the inputs that are running through this rule. When they first enter the AJAX logic, we need to cache their value. When the AJAX request returns, we check to see if the field value is the same as the cached one. If that's the case, then we know that this field is coming out of an AJAX checking state and we can look at the valid state as set by the server. If it's NOT the same value as the cached value, then we know this is NOT the return from an AJAX loop and we need to start the validation all over again.

The message logic also has to be adjusted so that we know if we are checking, or if the field is in fact invalid. Since the validation logic gets so heavy, I move this into it's own 'validate.js' file.

validate.js

  (function() {

    var validator = $("#register-form").kendoValidator({

      rules: {

        matches: function(input) {

          var matches = input.data('matches');

          // if the `data-matches attribute was found`
          if (matches) {

            // get the input to match
            var match = $(matches);

            // trim the values and check them
            if ( $.trim(input.val()) === $.trim(match.val()) )  {

              // the fields match
              return true;

            } else {

              // the fields don't match - validation fails
              return false;

            }

          }

          // don't perform any match validation on the input
          return true;

        },

        available: function(input) {

          var validate = input.data('available');

          // if the input has a `data-available` attribute...
          if (typeof validate !== 'undefined' && validate !== false) {

            // cache the field's id
            var id = input.attr('id');

            // new up a new cache object for this field if one doesn't already eist
            var cache = availability.cache[id] = availability.cache[id] || {};

            // set our status to checking
            cache.checking = true;

            // pull the url and message off of the proper data attributes
            var settings = {
              url: input.data('availableUrl') || '',
              message: kendo.template(input.data('availableMsg')) || ''
            };

            // if the value in the cache and the current input value are the same
            // and the cached state is valid...
            if (cache.value === input.val() && cache.valid) {

              // the value is available

              return true;

            }


            // if the value in the cache and the input value are the same 
            // and the cached state is not valid...
            if (cache.value === input.val() && !cache.valid) {

              // the value is not available

              cache.checking = false;
              return false;

            }

            // go to the ajax check
            availability.check(input, settings);

            // return false which goes into 'checking...' mode
            return false;

          }

          // this rule does not apply to the input
          return true;

        }
      },

      messages: {

        // custom error messages. email gets picked up 
        // automatically for any inputs of that type
        matches: function(input) {
          return input.data('matchesMsg');
        },
        email: 'That doesn\'t appear to be a valid email address',
        availability: function(input) {

          var id = input.attr('id');
          var msg = kendo.template(input.data('availableMsg') || '');
          var cache = availability.cache[id];

          if (cache.checking) {
            return "Checking..."
          }

          else {

            return msg(input.val());

          }
        }
      }

    }).data('kendoValidator');

    var availability = {

      cache: {},

      check: function(element, settings) {

        var id = element.attr('id');
        var cache = this.cache[id] = this.cache[id] || {};

        $.ajax({
          url: settings.url,
          dataType: 'json',
          data: { value: element.val() },

          success: function(data) {

            // the `data` object returns true or false
            // based on the availability of the value


            // set the value on the cache object so 
            // that it can be referenced in the next validation run
            cache.valid = data;

          },

          failure: function() {

            // the ajax call failed so just set the field
            // as valid since we don't know for sure that it's not
            cache.valid = true;
          },

          complete: function() {
            
            // trigger validation again
            validator.validateInput(element);

            // cache the inputs value
            cache.value = element.val();
         }
       });
     }
   };
}());

Notice that I handle the failure and complete events in the AJAX request as well. If the AJAX request fails, just mark the field as valid. That's all you can do. You might want to adjust the timeout as well so that the user doesn't sit there for a long time wondering why they can't submit the form and the field just says "checking".

Try it out for yourself. The below form has more fields, but contains both of the validation rules talked about in this article. Try using testuser as the username and watch it run through the validation rule using AJAX to check with the server. Pretty nifty!

Validation And Politics

It always starts out easy peasy, but then it begins to get pretty darn complex. I still maintain that one of the hardest things that a web developer can be asked to do is create a working form that is pleasant to use. It's not easy, but the Kendo UI Validator covers most of your basis and as you've seen from this article, you can extend it pretty far to cover all of your validation needs.

Download Kendo UI and try out the validator on your own forms. And remember - if this whole 'web development' thing never takes off, there's always a future in politics!


Burke Holland is the Director of Developer Relations at Telerik
About the Author

Burke Holland

Burke Holland is a web developer living in Nashville, TN and was the Director of Developer Relations at Progress. He enjoys working with and meeting developers who are building mobile apps with jQuery / HTML5 and loves to hack on social API's. Burke worked for Progress as a Developer Advocate focusing on Kendo UI.

Comments

Comments are disabled in preview mode.