JavaScript Validation with jQuery Validation and Knockout

Summary

Working with ASP.NET MVC when developing web applications you get a lot of validation support almost for free when you use the Unobtrusive Client Validation feature (which is enabled by default). Once you start introducing client-side dynamic form elements using Knockout.js, or other similar JavaScript frameworks, you need to take some additional steps to get validation for the Knockout-generated elements. This post describes one possible approach using a very simple HTML page.

Demonstration Page

The demonstration page for this is a very simple representation of a hypothetical web application that records games played by players, including the player’s name and the score for a variable number of games. The fields for the player’s name are static in nature (meaning they’re always present on the page) while the list of games and scores are generated using Knockout.

The demonstration page displaying validation errors.

The complete source code for the demonstration page can be found at the end of this post.

Knockout and Data Binding

If you’re not familiar with Knockout, there’s an excellent tutorial available on the Knockout site. Briefly, Knockout provides a framework that makes it easy to perform data binding using HTML and view models in JavaScript.

In our simple case we have a view model that contains the player’s first and last name along with an array containing the played games and scores:

function GameViewModel() {
    var self = this;

    this.Name = '';
    this.Score = '';
}

function ViewModel() {
    var self = this;

    self.Games = ko.observableArray();

    self.AddGame = function () {
        self.Games.push(new GameViewModel());
    };

    self.RemoveGame = function (game) {
        self.Games.destroy(game);
    }
}

The GameViewModel class represents a single played game while the ViewModel class serves as the main view model for the page and contains the player’s name along with the list of played games. We use the Knockout observable array feature here so the page is updated when games are added to or removed from the list of games.

To display the played games we use the data binding features of Knockout:

<span class="hljs-tag"><<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table table-condensed"</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">thead</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">tr</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">th</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:50%"</span>></span>Game<span class="hljs-tag"></<span class="hljs-name">th</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">th</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:30%"</span>></span>Score<span class="hljs-tag"></<span class="hljs-name">th</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">th</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:20%"</span>></span><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-default"</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"click: AddGame"</span>></span>Add Game<span class="hljs-tag"></<span class="hljs-name">button</span>></span><span class="hljs-tag"></<span class="hljs-name">th</span>></span>
        <span class="hljs-tag"></<span class="hljs-name">tr</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">thead</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">tbody</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"foreach: Games"</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">tr</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">td</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control validate-name-required"</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"value: Name, uniqueName: true"</span> /></span>
            <span class="hljs-tag"></<span class="hljs-name">td</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">td</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control validate-score"</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"value: Score, uniqueName: true"</span> /></span>
            <span class="hljs-tag"></<span class="hljs-name">td</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">td</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-xs btn-danger"</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"click: $root.RemoveGame"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"X"</span> /></span>
            <span class="hljs-tag"></<span class="hljs-name">td</span>></span>
        <span class="hljs-tag"></<span class="hljs-name">tr</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">tbody</span>></span>
<span class="hljs-tag"></<span class="hljs-name">table</span>></span>

Note the use of the Knockout uniqueName function in the data-binding. We use this to have Knockout generate a unique name for each field which is needed since jQuery Validate will only validate fields that have the name attribute set.

Validation

The first and last name of the player we validate by simply including the required attribute, jQuery Validate automatically picks this up when we tell it to validate the form:

<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-2 control-label"</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"FirstName"</span>></span>First Name:<span class="hljs-tag"></<span class="hljs-name">label</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-6"</span>></span><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"FirstName"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span> <span class="hljs-attr">required</span>=<span class="hljs-string">"required"</span> /></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-2 control-label"</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"LastName"</span>></span>Last Name:<span class="hljs-tag"></<span class="hljs-name">label</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-6"</span>></span><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"LastName"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span> <span class="hljs-attr">required</span>=<span class="hljs-string">"required"</span> /></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>

To get the Knockout-generated fields to be validate we use a different approach. One of several ways you can identify fields to validate is to define validation rules for fields that have specific CSS classes applied to them. In our case we have two distinct fields to validate in the table generated by Knockout:

  • The name of the played game, this field is required, but we want a specific error message if the validation fails
  • The score of the played game, if specified this must be an integer between 0 and 25

To start with we define to separate CSS classes that we’ll use to identify the fields in question:

<span class="hljs-tag"><<span class="hljs-name">style</span>></span><span class="css">
    <span class="hljs-selector-class">.validate-name-required</span> {
    }

    <span class="hljs-selector-class">.validate-score</span> {
    }
</span><span class="hljs-tag"></<span class="hljs-name">style</span>></span>

As you can see we don’t actually define any particular styling in these two classes (although you could if you wanted to), they’re simply used for identification purposes.

The next step is to define our custom validation rules:

    function DefineValidationRules() {
        $.validator.addMethod("gameName", $.validator.methods.required, "The name of the Game must be specified");
        $.validator.addMethod("gameScore", ValidateInteger, "The Score must be an integer");
        $.validator.addMethod("gameScoreMin", $.validator.methods.min, $.format("The Score must be greater than or equal to {0}"));
        $.validator.addMethod("gameScoreMax", $.validator.methods.max, $.format("The Score value must be less than or equal to {0}"));

        $.validator.addClassRules("validate-name-required", { gameName: true });
        $.validator.addClassRules("validate-score", { gameScore: true, gameScoreMin: 0, gameScoreMax: 25 });
    }

First we define the 4 separate validation rule methods that will be used. Each rule method is defined by a name (which must be a valid JavaScript identifier), the actual function that provides the functionality for the rule and the message to display if the validation fails.

For the gameNamegameScoreMin and gameScoreMax rules we use standard functions from jQuery Validate, but the gameScore rule uses a custom function since the built-in rule for numbers validates both integers and decimal numbers and we only want integers to be considered valid:

function ValidateInteger(value, element, param) {
    if (!value) {
        // Null and empty strings are invalid as far as we are concerned.
        return false;
    }

    var parsedNumber = parseInt(value);

    if (isNaN(parsedNumber)) {
        return false;
    }
    else {
        return (value == parsedNumber);
    }
}

Once the rule methods have been defined we next define the actual validation rules to apply by defining the CSS classes to look for and which rule methods to apply to the corresponding elements. The gameScoreMin and gameScoreMax rule methods illustrates how the rules can be parameterized so the same rule method can be used in different rules.

The last step required at this point is to tie everything up and initialize the page:

$(document).ready(InitializeForm);

function InitializeForm() {
    $.validator.setDefaults({
        submitHandler: function () { alert('Submitted'); }
    });

    DefineValidationRules();

    ko.applyBindings(new ViewModel());
    $('#gameInfo').validate();
}

When the page is ready we:

  1. Override the standard form submit handler as we’re doing everything locally without actually submitting any data to a server
  2. Call the function that defines our custom validation rules
  3. Initialize the Knockout data binding
  4. Tell jQuery Validate which form to validate so that when that form is submitted it will first be validated

That’s pretty much all there is to get this working. It should be noted that jQuery Validate is very flexible and there are several other ways to accomplish validation, but this approach is fairly straight-forward and easy to understand.

Source Code

Below is the complete source for the self-contained HTML file that demonstrates the validation. You can also view it on GitHub.

<span class="hljs-meta"><!DOCTYPE html></span>

<span class="hljs-tag"><<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/1999/xhtml"</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">head</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /></span>
        <span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css"</span> /></span>
        <span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables_themeroller.css"</span> /></span>
        <span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.css"</span> /></span>
        <span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap-theme.css"</span> /></span>
        <span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"http://netdna.bootstrapcdn.com/bootswatch/3.0.0/amelia/bootstrap.min.css"</span> /></span>
        <span class="hljs-tag"><<span class="hljs-name">title</span>></span>Validation Test<span class="hljs-tag"></<span class="hljs-name">title</span>></span>

        <span class="hljs-tag"><<span class="hljs-name">style</span>></span><span class="css">
            <span class="hljs-selector-class">.validate-name-required</span> {
            }

            <span class="hljs-selector-class">.validate-score</span> {
            }
        </span><span class="hljs-tag"></<span class="hljs-name">style</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">head</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">body</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"margin:24pt;"</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"panel panel-default"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"panel-heading"</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">h2</span>></span>Validation with jQuery Validate and Knockout.JS<span class="hljs-tag"></<span class="hljs-name">h2</span>></span>
            <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"panel-body"</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">form</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-horizontal"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"gameInfo"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"get"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">""</span>></span>
                    <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>></span>
                        <span class="hljs-tag"><<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-2 control-label"</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"FirstName"</span>></span>First Name:<span class="hljs-tag"></<span class="hljs-name">label</span>></span>
                        <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-6"</span>></span><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"FirstName"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span> <span class="hljs-attr">required</span>=<span class="hljs-string">"required"</span> /></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                    <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                    <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>></span>
                        <span class="hljs-tag"><<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-2 control-label"</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"LastName"</span>></span>Last Name:<span class="hljs-tag"></<span class="hljs-name">label</span>></span>
                        <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-6"</span>></span><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"LastName"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span> <span class="hljs-attr">required</span>=<span class="hljs-string">"required"</span> /></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                    <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                    <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>></span>
                        <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-2"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                        <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-10"</span>></span>
                            <span class="hljs-tag"><<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table table-condensed"</span>></span>
                                <span class="hljs-tag"><<span class="hljs-name">thead</span>></span>
                                    <span class="hljs-tag"><<span class="hljs-name">tr</span>></span>
                                        <span class="hljs-tag"><<span class="hljs-name">th</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:50%"</span>></span>Game<span class="hljs-tag"></<span class="hljs-name">th</span>></span>
                                        <span class="hljs-tag"><<span class="hljs-name">th</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:30%"</span>></span>Score<span class="hljs-tag"></<span class="hljs-name">th</span>></span>
                                        <span class="hljs-tag"><<span class="hljs-name">th</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:20%"</span>></span><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-default"</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"click: AddGame"</span>></span>Add Game<span class="hljs-tag"></<span class="hljs-name">button</span>></span><span class="hljs-tag"></<span class="hljs-name">th</span>></span>
                                    <span class="hljs-tag"></<span class="hljs-name">tr</span>></span>
                                <span class="hljs-tag"></<span class="hljs-name">thead</span>></span>
                                <span class="hljs-tag"><<span class="hljs-name">tbody</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"foreach: Games"</span>></span>
                                    <span class="hljs-tag"><<span class="hljs-name">tr</span>></span>
                                        <span class="hljs-tag"><<span class="hljs-name">td</span>></span>
                                            <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control validate-name-required"</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"value: Name, uniqueName: true"</span> /></span>
                                        <span class="hljs-tag"></<span class="hljs-name">td</span>></span>
                                        <span class="hljs-tag"><<span class="hljs-name">td</span>></span>
                                            <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control validate-score"</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"value: Score, uniqueName: true"</span> /></span>
                                        <span class="hljs-tag"></<span class="hljs-name">td</span>></span>
                                        <span class="hljs-tag"><<span class="hljs-name">td</span>></span>
                                            <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-xs btn-danger"</span> <span class="hljs-attr">data-bind</span>=<span class="hljs-string">"click: $root.RemoveGame"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"X"</span> /></span>
                                        <span class="hljs-tag"></<span class="hljs-name">td</span>></span>
                                    <span class="hljs-tag"></<span class="hljs-name">tr</span>></span>
                                <span class="hljs-tag"></<span class="hljs-name">tbody</span>></span>
                            <span class="hljs-tag"></<span class="hljs-name">table</span>></span>
                        <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                    <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                    <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-lg btn-primary"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Submit"</span> /></span>
                <span class="hljs-tag"></<span class="hljs-name">form</span>></span>
            <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
        <span class="hljs-tag"></<span class="hljs-name">div</span>></span>

        <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/jquery-ui.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>

        <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript">
            <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GameViewModel</span>() </span>{
                <span class="hljs-keyword">var</span> self = <span class="hljs-keyword">this</span>;

                <span class="hljs-keyword">this</span>.Name = <span class="hljs-string">''</span>;
                <span class="hljs-keyword">this</span>.Score = <span class="hljs-string">''</span>;
            }

            <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ViewModel</span>() </span>{
                <span class="hljs-keyword">var</span> self = <span class="hljs-keyword">this</span>;

                self.Games = ko.observableArray();

                self.AddGame = <span class="hljs-function"><span class="hljs-keyword">function</span> () </span>{
                    self.Games.push(<span class="hljs-keyword">new</span> GameViewModel());
                };

                self.RemoveGame = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">game</span>) </span>{
                    self.Games.destroy(game);
                }
            }

            <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ValidateInteger</span>(<span class="hljs-params">value, element, param</span>) </span>{
                <span class="hljs-keyword">if</span> (!value) {
                    <span class="hljs-comment">// Null and empty strings are invalid as far as we are concerned.</span>
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
                }

                <span class="hljs-keyword">var</span> parsedNumber = <span class="hljs-built_in">parseInt</span>(value);

                <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(parsedNumber)) {
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
                }
                <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">return</span> (value == parsedNumber);
                }
            }

            <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">DefineValidationRules</span>() </span>{
                $.validator.addMethod(<span class="hljs-string">"gameName"</span>, $.validator.methods.required, <span class="hljs-string">"The name of the Game must be specified"</span>);
                $.validator.addMethod(<span class="hljs-string">"gameScore"</span>, ValidateInteger, <span class="hljs-string">"The Score must be an integer"</span>);
                $.validator.addMethod(<span class="hljs-string">"gameScoreMin"</span>, $.validator.methods.min, $.format(<span class="hljs-string">"The Score must be greater than or equal to {0}"</span>));
                $.validator.addMethod(<span class="hljs-string">"gameScoreMax"</span>, $.validator.methods.max, $.format(<span class="hljs-string">"The Score value must be less than or equal to {0}"</span>));

                $.validator.addClassRules(<span class="hljs-string">"validate-name-required"</span>, { <span class="hljs-attr">gameName</span>: <span class="hljs-literal">true</span> });
                $.validator.addClassRules(<span class="hljs-string">"validate-score"</span>, { <span class="hljs-attr">gameScore</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">gameScoreMin</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">gameScoreMax</span>: <span class="hljs-number">25</span> });
            }

            $(<span class="hljs-built_in">document</span>).ready(InitializeForm);

            <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">InitializeForm</span>() </span>{
                $.validator.setDefaults({
                    <span class="hljs-attr">submitHandler</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> () </span>{ alert(<span class="hljs-string">'Submitted'</span>); }
                });

                DefineValidationRules();

                ko.applyBindings(<span class="hljs-keyword">new</span> ViewModel());
                $(<span class="hljs-string">'#gameInfo'</span>).validate();
            }
        </span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">body</span>></span>
<span class="hljs-tag"></<span class="hljs-name">html</span>></span>
CSG Pro

CSG Pro

Our data analytics consultants solve problems and ignite victories. We help you understand your data so you can make smarter business decisions. We build custom software applications that strengthen your process and your team.
SHARE THIS
Share on twitter
Share on facebook
Share on linkedin
Share on email
RELATED POSTS
Stay Connected

Subscribe to CSG Pro. We’ll supply your inbox with the latest blog posts, training videos, and upcoming events.

Search

Ready to wrangle your data?