Last active
September 22, 2015 18:45
-
-
Save infocynic/cfbc8b4ef8911411b627 to your computer and use it in GitHub Desktop.
Adds Knockout 3 bindings for rendering ASP.Net MVC input and validation markup for indexed primitive and complex types.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Assigns an ASP.Net MVC-style name attribute to a form field that is part of a list. | |
// Used inside a foreach: binding. | |
// Example for an indexed primitive type (string, int, etc.) : | |
// <div data-bind="foreach: Items"> | |
// <input type="text" data-bind="value: SomeValue, aspnetIndexedName: 'Items'" /> | |
// </div> | |
// | |
// The above will name the input fields "Items[0]", "Items[1]", etc. For a complex type, | |
// a property can be specified in addition to the name: | |
// <div data-bind="foreach: Items"> | |
// <input type="text" data-bind="value: FirstName, aspnetIndexedName: { name: 'Items', property: 'FirstName' }" /> | |
// </div> | |
// or using a shorthand name.property form: | |
// <input type="text" data-bind="value: FirstName, aspnetIndexedName: 'Items.FirstName' }" /> | |
// | |
// The above will name the input fields "Items[0].FirstName", "Items[1].FirstName", etc. | |
// | |
// The numeric index value defaults to the $index property of the binding context, but can be specified | |
// in the parameters: | |
// <div data-bind="foreach: Items"> | |
// <input type="text" data-bind="value: FirstName, aspnetIndexedName: { name: 'Items', property: 'FirstName', index: Line }" /> | |
// </div> | |
// | |
// For this to work properly with the ASP.Net MVC model binder, the index values must start at 0 and increase by 1; | |
// the sequence cannot omit any values. | |
// | |
// To render validation errors, the ASP.Net MVC model state must be available through the viewmodel. One way to | |
// do this is: | |
// function MyViewModel { | |
// var self = this; | |
// ... | |
// // Serialize the model state to capture the validation messages. | |
// self.modelState = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(ViewData.ModelState)); | |
// } | |
;(function($) { | |
ko.bindingHandlers.aspnetIndexedName = { | |
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { | |
var name, property, index; | |
var bindings = allBindings().aspnetIndexedName; | |
// If just a string, parse it as "name.property". For primitive values, property is empty. | |
if (typeof bindings === "string") { | |
var parts = bindings.split("."); | |
name = parts[0]; | |
property = parts[1]; | |
index = bindingContext.$index(); | |
} | |
else { | |
name = bindings.name; | |
property = bindings.property; | |
index = bindings.index || bindingContext.$index(); | |
} | |
var fullname = name + '[' + index + ']'; | |
if (property) | |
fullname = fullname + '.' + property; | |
$(element).attr({ name: fullname, id: fullname }); | |
// Is there a modelstate error? | |
var modelStateProperty = bindings.modelState || 'modelState'; | |
var modelState = bindingContext.$root[modelStateProperty]; | |
if (modelState && modelState[fullname] && modelState[fullname].Errors.length) { | |
$(element).addClass("input-validation-error"); | |
} | |
} | |
} | |
// Populates an ASP.Net MVC validation control that is rendered within a foreach: binding. The | |
// data-valmsg-for attribute is given the name of the input control, and the validation | |
// message is inserted. | |
// The validation messages must be serialized into the viewmodel. | |
// | |
// Example markup: | |
// <div data-bind="foreach: Items"> | |
// <input type="text" data-bind="value: FirstName, aspnetIndexedName: 'Items.FirstName' }" /> | |
// <span class="control-label" data-bind="aspnetIndexedValidation: 'Items.FirstName'}" /> | |
// </div> | |
// | |
// Example view model: | |
// function MyViewModel { | |
// var self = this; | |
// ... | |
// // Serialize the model state to capture the validation messages. | |
// self.modelState = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(ViewData.ModelState)); | |
// } | |
ko.bindingHandlers.aspnetIndexedValidation = { | |
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { | |
var name, index, property; | |
var bindings = allBindings().aspnetIndexedValidation; | |
// If parameters are just a string, it's the name | |
if (typeof bindings === "string") { | |
var parts = bindings.split("."); | |
name = parts[0]; | |
property = parts[1]; | |
index = bindingContext.$index(); | |
} | |
else { | |
name = bindings.name; | |
property = bindings.property; | |
index = bindings.index || bindingContext.$index(); | |
} | |
var fullname = name + '[' + index + ']'; | |
if (property) | |
fullname = fullname + '.' + property; | |
$elem = $(element); | |
$elem.attr({ 'data-valmsg-for': fullname }); | |
$elem.attr({ 'data-valmsg-replace': true }); | |
var modelStateProperty = bindings.modelState || 'modelState'; | |
// Insert initial error message | |
var modelState = bindingContext.$root[modelStateProperty]; | |
if (modelState && modelState[fullname] && modelState[fullname].Errors.length) { | |
$elem.text(modelState[fullname].Errors[0].ErrorMessage); | |
$elem.addClass("field-validation-error"); | |
} | |
else { | |
$elem.addClass("field-validation-valid") | |
} | |
}, | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Renamed file so it can be picked up with a ScriptBundle -{version}; added closure for jQuery.