KnockoutJS is a JavaScript plugin with Model-View-View-Model (MVVM) pattern. When your data model's state changes, your UI updates automatically. Like AngularJS, it provides full control for a View and its Model.
In MVC, there are a lot of Data Annotations to use in our Models. The goal here is to create custom ones for KnockoutJS.
Let's get started!
What do we need?
- MVC application.
- DataBind Attribute classes for those KnockoutJS annotations.
- EnableDataBind: enables or disables a field.
- MaskDataBind: input masks for a field.
- DateDataBind: transforms an input into a date mask field.
- OptionsDataBind: configures a select field with options.
- HtmlHelper to render an input with those annotations.
- InputFor: renders an input for KnockoutJS.
- SelectFor: renders a select for KnockoutJS.
The front-end
All the fields above are "handled" by KnockoutJS. I have configured some data annotations for Person Model, as shown below.
- public sealed class Person
- {
- [Display(Name = "Name")]
- [Required]
- [ValueDataBind("person.Name")]
- [CssClassTag("form-control")]
- public string Name { get; set; }
-
- [ValueDataBind("person.Id")]
- public string Id { get; set; }
-
- [Display(Name = "Email")]
- [Required]
- [EmailAddress]
- [EnableDataBind("isEnableToEdit")]
- [ValueDataBind("person.Email")]
- [CssClassTag("form-control")]
- public string Email { get; set; }
-
- [Display(Name = "Bith Date")]
- [DateDataBind("person.BithDate")]
- [CssClassTag("form-control")]
- public DateTime? BithDate { get; set; }
-
- [Display(Name = "Gender")]
- [EnableDataBind("isEnableToEdit")]
- [OptionsDataBind("genders", "person.Gender")]
- [CssClassTag("form-control")]
- public int? Gender { get; set; }
- }
In HTML, the code will be something like this.
Views/Person/Index.cshtml
- @Html.InputFor(x => x.Person.Name)
- @Html.InputFor(x => x.Person.BithDate)
- @Html.SelectFor(x => x.Person.Gender)
- @Html.InputFor(x => x.Person.Email)
It will render the following.
- <input class="text-box single-line, form-control" data-val="true" data-val-required="The field Name is required." id="Person_Name" name="Person.Name" type="text" value="" data-bind="value: person.Name">
-
- <input class="text-box single-line, form-control" data-val="true" data-val-date="The field Bith Date must be a date." id="Person_BithDate" name="Person.BithDate" type="text" value="" data-bind="value: person.BithDate, dateValue: person.BithDate, mask: { mascara: '99/99/9999', tipo: 'Date', value: person.BithDate }">
-
- <select id="Person_Gender" name="Person.Gender" data-bind="enable: isEnableToEdit, options: genders, optionsText: 'Name', optionsValue:'Id', value: person.Gender" class="form-control"></select>
-
- <input class="text-box single-line, form-control" data-val="true" data-val-email="O campo Email não é um endereço de email válido." data-val-required="The field Email is required" id="Person_Email" name="Person.Email" type="email" value="" data-bind="enable: isEnableToEdit, value: person.Email">
The JavaScript file is shown below. It is used to control some data in KnockoutJS for the front-end. The "isEnableToEdit" computed function is used to enable DataBind annotation in MVC Person Model.
Scripts/view-models/person.js
- var personViewModel = function () {
-
- var _vm = null,
-
- createComputed = function () {
- _vm.isEnableToEdit = ko.computed(function () {
- return (_vm.person.Name() || '').length > 2;
- }, _vm);
- },
-
- init = function (model) {
- _vm = {
- person: ko.mapping.fromJS(model.Person),
- genders: [
- { Id: 0, Name: 'Select...' },
- { Id: 1, Name: 'Masc' },
- { Id: 2, Name: 'Fem' }
- ]
- };
-
- createComputed();
-
- var ctx = $('#person').get(0);
- ko.applyBindings(_vm, ctx);
- }
-
- return {
- init: init
- }
-
- }();
Download the full source code from the below link.
FSL.MvcDataAnnotationsHtmlHelpersKnockoutJS
Remark
I will update that repository with new annotations and HTML Helpers features.