65.9K
CodeProject is changing. Read more.
Home

Customer KnockoutJS Validation

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (6 votes)

Jul 19, 2012

CPOL

2 min read

viewsIcon

55118

A KnockoutJS plug-in for model and property validation that is named Knockout validation

This article is the second on the KnockoutJS series I am writing. The first article was introductory: Customer KnockoutJS and MVC demo using JSON.

Now I am going to focus on KnockoutJS and validation. I am going to use a KnockoutJS plug-in for model and property validation that is named Knockout Validation. You can download it from here.

The ASP.NET MVC Controller has the actions to Get and Add a customer. This example has a new customer property: the country. This property allows to add an input type select to KnockoutJS and validation.

namespace KnockoutDemo.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "";

            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public JsonResult Get(int customerID)
        {
            // Get the customer ...
            Customer customer = new Customer {CustomerID = customerID, 
                                              FirstName = "John", 
                                              LastName = "Doe", 
                                              IsMale = true, 
                                              CountryID = 1 };
            return Json(customer);
        }

        [HttpPost]
        public JsonResult Add(Customer customer)
        {
            // Save the customer ...

            // return status message 
            var message = string.Format("Customer: {0} {1} Added. IsMale: {2} Age:{3}  CountryID: {4} ",
                                        customer.FirstName, customer.LastName, 
                                        customer.IsMale.ToString(), 
                                        customer.Age.ToString(), customer.CountryID.ToString());
            return Json(message);
        }
    }
}

The ASP.NET MVC model is the customer with the new property:

namespace KnockoutDemo.Models
{
    public class Customer
    {

        public int CustomerID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool IsMale { get; set; }
        public int Age { get; set; }
        public int CountryID { get; set; }
    }
}

The ASP.NET MVC Layout includes the new knockout validation plug in:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" 
    rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" 
    type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" 
    type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/knockout.js")" 
    type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/json2.js")" 
    type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/knockout.validation.js")" 
    type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <header>
            <div id="title">
                <h1>Knockout Demo</h1>
            </div>
            <div> </div>
        </header>
        <div> </div>
        <div> </div>
        <section id="main">
            @RenderBody()
        </section>
        <footer>
        </footer>
    </div>
</body>
</html>

And the ASP.NET MVC view has the KnockoutJS and validation specifics:

@{
    ViewBag.Title = "Add Customer";
}
<!-- This should go to a css file-->
<style type="text/css">
.errorFill
{
    border:1px solid red;
    background-color: #fdd;
}
</style>

<h2>@ViewBag.Message</h2>

<form method="post" action="">

    <b>Customer Number: </b> <span data-bind="text: 
    CustomerID" ></span><br /><br />
    <b>First Name: </b><input type="text" 
    data-bind="value: FirstName" style="width:200px" /> <br /><br />
    <b>Last Name: </b><input type="text" 
    data-bind="value: LastName" style="width:200px" /> <br /><br />
    <b>Age: </b><input type="text" 
    data-bind="value: Age" style="width:200px" /> <br /><br />
    <input type="checkbox" data-bind="checked: 
    IsMale" /><b>Male</b><br /><br />
    <b>Country: </b><select data-bind="options: CountryOptions, 
    optionsValue: 'CountryID', 
          optionsText: 'Name', value:CountryID, 
          optionsCaption:'-- Country --'"></select> <br /><br />
    <br />
    <input type="button" data-bind="click: 
    KnockoutDemoNamespace.addCustomer" value="Add Customer" />
    <br />
    <div id="message"></div>
</form>

<script type="text/javascript">
// Initialized the namespace
var KnockoutDemoNamespace = {};

// View model declaration
KnockoutDemoNamespace.initViewModel = function (customer, countries) {
    var customerViewModel = ko.validatedObservable({
        CustomerID: ko.observable(customer.CustomerID),
        FirstName: ko.observable(customer.FirstName).extend({ required: true }),
        LastName: ko.observable(customer.LastName).extend({ required: true }),
        IsMale: ko.observable(customer.IsMale),
        Age: ko.observable(customer.Age).extend({ required: true }).extend({ number: true }),
        CountryID: ko.observable(customer.CountryID).extend({ required: true }),
        CountryOptions: ko.observableArray(countries)
    });

    var validationOptions = 
      { insertMessages: true, decorateElement: true, errorElementClass: 'errorFill' };
    ko.validation.init(validationOptions);

    return customerViewModel;
}

// Bind the customer
KnockoutDemoNamespace.bindData = function (customer) {
    // get the country list
    KnockoutDemoNamespace.getCountries();
    
    // Create the view model
    KnockoutDemoNamespace.viewModel = 
      KnockoutDemoNamespace.initViewModel(customer, KnockoutDemoNamespace.countries);
    ko.applyBindings(this.viewModel);
}

KnockoutDemoNamespace.getCountries = function () {
    $.ajax({
        url: "/Country/",
        type: 'post',
        contentType: 'application/json',
        cache: false,
        async: false,
        success: function (result) {
            KnockoutDemoNamespace.countries = result;
        },
        error: function (jqXHR, textStatus, errorThrown) {
            var errorMessage = '';
            $('#message').html(jqXHR.responseText);
        }
    });
}

KnockoutDemoNamespace.getCustomer = function (customerID) {
    $.ajax({
        url: "/Home/Get/",
        type: 'post',
        data: "{'customerID':'1' }",
        contentType: 'application/json',
        cache: false,
        success: function (result) {            
            KnockoutDemoNamespace.bindData(result);
        },
        error: function (jqXHR, textStatus, errorThrown) {
            var errorMessage = '';
            $('#message').html(jqXHR.responseText);
        }
    });
}

KnockoutDemoNamespace.addCustomer = function () {
    
    if (KnockoutDemoNamespace.viewModel.isValid()) {
        $.ajax({
            url: "/Home/Add/",
            type: 'post',
            data: ko.toJSON(this),
            contentType: 'application/json',
            success: function (result) {
                $('#message').html(result);
            }
        });
    }
}

$(document).ready(function () {
    KnockoutDemoNamespace.getCustomer(1);

});

</script>

The KnockoutJS now has a observableArray of countries in the ViewModel, that is bind to the country select. Note the data-bind options of the select:

  • options: Controls what options should appear in a drop-down list
  • optionsValue: The name of the ViewModel property to bind to the option value
  • optionsText: The name of the ViewModel property to bind to the option text
  • value: The name of the ViewModel property to bind to the selected value
  • optionsCaption: The option that is used to make the user select a value on the select list

There is also the validation plug-in specifics in the ViewModel declaration:

  • The extend is used to extend the observables with the validation rules (you can read about all of them here).

In the example, I am using the required (required: true) and number (number: true) validation rules.

  • validatedObservable in the view model declaration
  • isValid() - Before saving the data, validate that the model is valid
  • validation.init - Used to configure the validation engine

In the example, the invalid inputs are painted in red, that is where the CSS class is used by the validation.