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)
{
Customer customer = new Customer {CustomerID = customerID,
FirstName = "John",
LastName = "Doe",
IsMale = true,
CountryID = 1 };
return Json(customer);
}
[HttpPost]
public JsonResult Add(Customer customer)
{
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";
}
<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.