MVC 3 validation: want to turn my labels red that correspond to controls that failed validation - asp.net-mvc-3

I am using MVC 3 validation. My Product Manager wants the label for each control that has an error to turn red.
So 'Student First Name' label should turn red. 'Email address' label should turn red.
I tried to wrap each error msg in a div and check the length of each div
<div id="divValStudentFirstName">#Html.ValidationMessageFor(m => m.studentFirstName)</div>
in a js file:
$(document).ready(function () {
if ($("#divValStudentFirstName").length > 1) {
("#divStudentFirstName").css("color", "red");
}
But I have no success. The validation check is done without a complete refresh and as a result, my $(document).ready isn't fired when validation hits.

Client side validation disabled :
public static IHtmlString ValidationLabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText = null) {
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var name = ExpressionHelper.GetExpressionText(expression);
string resolvedLabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName ?? name.Split('.').Last();
if (String.IsNullOrEmpty(resolvedLabelText)) {
return MvcHtmlString.Empty;
}
var tag = new TagBuilder("label");
tag.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name)));
tag.GenerateId(name);
tag.SetInnerText(resolvedLabelText);
ModelState modelState;
string fullName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (html.ViewData.ModelState.TryGetValue(fullName, out modelState)) {
if (modelState.Errors.Count > 0) {
tag.Attributes.Add("style", "color:red");
}
}
return new MvcHtmlString(tag.ToString());
}
EDIT
Client side validation enabled
I'm really not a king in js, but this seems to work (well on a simple case at least)
$('form').submit(function () {
var form = $(this);
$('label').removeClass('field-validation-error');
if (!form.valid()) {
$('.input-validation-error')
.each(function () {
$("label[for='" + $(this).attr("id") + "']").addClass('field-validation-error');
});
}
});

Below is a JQuery function that will color the containing div tag by adding (or removing) an error class when the submit button is clicked. It can easily be changed to add the class to the label.
$('form').submit(function () {
if ($(this).valid()) {
$(this).find('div.form-group').each(function () {
if ($(this).find('span.field-validation-error').length == 0) {
$(this).removeClass('error-colored');
}
});
}
else {
$(this).find('div.form-group').each(function () {
if ($(this).find('span.field-validation-error').length > 0) {
$(this).addClass('error-colored');
}
});
}
});

Related

how to add unique username in kendo ui and show alert or message after this insert duplicate username

I want to use kendo ui for manage the users in asp.net mvc and i need that kendo ui don't create duplicate username and display the error message that "the user name is duplicate"
this is my action for create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddNewCountry([DataSourceRequest]DataSourceRequest request, CountryViewModel c,int countryId)
{
if (c != null && ModelState.IsValid)
{
countryService.Create(c);
}
return Json(new[] { c }.ToDataSourceResult(request, ModelState));
}
Thanks in advance for your help
I sent you a solution in your other post (see here). But here is how you handle SERVER side errors using the kendo CLIENT grid. Some of these steps could be changed. For example, you could popup an alert instead of displaying the error on the editor template.
1) Add a model state error to your action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddNewCountry([DataSourceRequest]DataSourceRequest request, CountryViewModel c,int countryId)
{
if (countryService.UserExists(c.UserName) // You need to code this
{
ModelState.AddModelError("UserName", "User name already exists.");
}
if (c != null && ModelState.IsValid)
{
countryService.Create(c);
}
return Json(new[] { c }.ToDataSourceResult(request, ModelState));
}
Handle the error event on your DataSource:
#(Html.Kendo().Grid<OrderDetailViewModel>()
.Name("orderDetailsGrid")
/* Not relevant grid setup code... */
.DataSource(dataSource => dataSource
.Ajax()
.Read(builder => builder.Url("/api/CustomerOrderDetails/GetOrderDetails/" + Model.OrderId).Type(HttpVerbs.Get))
.Create(builder => builder.Url("/api/CustomerOrderDetails/CreateOrderDetail/" + Model.OrderId).Type(HttpVerbs.Put))
.Update(builder => builder.Url("/api/CustomerOrderDetails/UpdateOrderDetail").Type(HttpVerbs.Post))
.Destroy(builder => builder.Url("/api/CustomerOrderDetails/DeleteOrderDetail").Type(HttpVerbs.Delete))
.Model(model => {
model.Id(x => x.OrderDetailId);
model.Field(m => m.OrderDetailId).DefaultValue(0);
})
.Events(events => events.Error("OrderDetails_Error"))
))
3) Add a placeholder for the errors to your editor template:
<ul class="errors"></ul>
4) Setup a kendo template to process the errors:
<script type="text/x-kendo-template" id="orderDetailsValidationMessageTemplate">
# if (messages.length) { #
<li>#=field#
<ul>
# for (var i = 0; i < messages.length; ++i) { #
<li>#= messages[i] #</li>
# } #
</ul>
</li>
# } #
</script>
Write the js error handler that will look at the server errors returned and format them into a template that can be displayed on the editor page:
OrderDetails_Error = function(args) {
if (args.errors) {
var grid = $("#orderDetailsGrid").data("kendoGrid");
var validationTemplate = kendo.template($("#orderDetailsValidationMessageTemplate").html());
grid.one("dataBinding", function(e) {
e.preventDefault();
$.each(args.errors, function(propertyName) {
// take the template and insert it into the placeholder
var renderedTemplate = validationTemplate({ field: propertyName, messages: this.errors });
grid.editable.element.find(".errors").append(renderedTemplate);
});
});
}
};

Kendo Grid always focus on first cell of Top Row

I have checkbox in Kendo grid. Once i click on Checkbox it always focus the top cell in Kendo Grid. Below is code for Kendo grid that I am binding to checkbox value on checkbox click event in Kendo Grid
$("#contactgrid").on('click', '.chkbx', function () {
var checked = $(this).is(':checked');
var grid = $('#contactgrid').data().kendoGrid;
var rowIdx = $("tr", grid.tbody).index(row);
var colIdx = $("td", row).index(this);
// grid.tbody.find("tr").eq(rowIndex).foucs(); This doesn't work
var dataItem = grid.dataItem($(this).closest('tr'));
dataItem.set('IsSelected', checked);
});
I can get the row index and cell Index in click event but I was not able to figure out to focus the specific cell.
Thanks!
When you want to edit Grid with checkbox then I would suggest you to use the approach from this code library. No matter it uses the MVC extensions open Views/Home/Index.cshtml and see how the template is defined and the javascript used after initializing the Grid.
Here it is
Column template:
columns.Template(#<text></text>).ClientTemplate("<input type='checkbox' #= IsAdmin ? checked='checked':'' # class='chkbx' />")
.HeaderTemplate("<input type='checkbox' id='masterCheckBox' onclick='checkAll(this)'/>").Width(200);
<script type="text/javascript">
$(function () {
$('#persons').on('click', '.chkbx', function () {
var checked = $(this).is(':checked');
var grid = $('#persons').data().kendoGrid;
var dataItem = grid.dataItem($(this).closest('tr'));
dataItem.set('IsAdmin', checked);
})
})
function checkAll(ele) {
var state = $(ele).is(':checked');
var grid = $('#persons').data().kendoGrid;
$.each(grid.dataSource.view(), function () {
if (this['IsAdmin'] != state)
this.dirty=true;
this['IsAdmin'] = state;
});
grid.refresh();
}
</script>
I struggled with this. I essential refocused the cell as shown below. There's plenty of room for improvement in the Kendo grid client-side API. Hopefully my helper methods below will help people out.
var $row = getRowForDataItem(this);
var $current = getCurrentCell($row);
var currentCellIndex = $row.find(">td").index($current);
this.set('PriceFromDateTime', resultFromDate);
$row = getRowForDataItem(this);
var grid = getContainingGrid($row);
//select the previously selected cell by it's index(offset) within the td tags
if (currentCellIndex >= 0) {
grid.current($row.find(">td").eq(currentCellIndex));
}
//Kendo grid helpers
function getColumn(grid, columnName) {
return $.grep(grid.columns, function (item) {
return item.field === columnName;
})[0];
}
function getRowForDataItem(dataItem) {
return $("tr[data-uid='" + dataItem.uid + "']");
}
function getCurrentCell($elem) {
return getContainingGrid($elem).current();
}
function getContainingDataItem($elem) {
return getDataItemForRow(getContainingRow($elem));
}
function getContainingCell($elem) {
return $elem.closest("td[role='gridcell']");
}
function getContainingRow($elem) {
return $elem.closest("tr[role='row']");
}
function getContainingGrid($elem) {
return $elem.closest("div[data-role='grid']").data("kendoGrid");
}
function getGridForDataItem(dataItem) {
return getContainingGrid(getRowForDataItem(dataItem));
}
function getDataItemForRow($row, $grid) {
if (!$grid) $grid = getContainingGrid($row);
return $grid.dataItem($row);
}
function getMasterRow($element) {
return $element.closest("tr.k-detail-row").prev();
}
function getChildGridForDataItem(dataItem) {
return getRowForDataItem(dataItem).next().find("div.k-grid").data("kendoGrid");
}
function getMasterRowDataItem($element) {
var $row = getMasterRow($element);
return getDataItemForRow($row);
}

JsonLogOn via https

I've just introduced SSL to my MVC website. I made the whole default AccountContorller use it. It works fine unless I'm currently on http page and try to log on with ajax (the logon action is vredirected to httpS). This popup logon window doesn't even show up.
For the controller a used a custom attribute:
public class RequireSSL : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionDescriptor.IsDefined(typeof(NoSSL), true) ||
filterContext.ActionDescriptor.IsDefined(typeof(OptionalSSL), true))
{
base.OnActionExecuting(filterContext);
return;
}
HttpRequestBase req = filterContext.HttpContext.Request;
HttpResponseBase res = filterContext.HttpContext.Response;
//Check if we're secure or not and if we're on the local box
if (!req.IsSecureConnection && (!req.IsLocal || Properties.Settings.Default.UseSSLForLocalRequests))
{
var builder = new UriBuilder(req.Url)
{
Scheme = Uri.UriSchemeHttps,
Port = Properties.Settings.Default.HttpsPort,
};
res.Redirect(builder.Uri.ToString());
}
base.OnActionExecuting(filterContext);
}
}
How can I make it work?
EDIT
The whole rest was generated by MVC. (Project with built in authentications)
That's the link.
#Html.ActionLink(#Labels.LogOn, "LogOn", "Account", routeValues: null, htmlAttributes: new { id = "logonLink", data_dialog_title = "Identification" })
JS somehow hooks into that link and performs ajax logon . Probably with this code: (JajaxLogin.js - also out of the box)
/// <reference path="jquery-1.6.2.js" />
/// <reference path="jquery.validate.js" />
$(function () {
// Cache for dialogs
var dialogs = {};
var getValidationSummaryErrors = function ($form) {
// We verify if we created it beforehand
var errorSummary = $form.data('validation-summary-errors');
if (!errorSummary) {
errorSummary = $('<div class="validation-summary-errors"><span>Please correct the errors and try again.</span><ul></ul></div>')
.insertBefore($form);
// Remember that we created it
$form.data('validation-summary-errors', errorSummary);
}
return errorSummary;
};
var formSubmitHandler = function (e) {
var $form = $(this);
// We check if jQuery.validator exists on the form
if (!$form.valid || $form.valid()) {
$.post($form.attr('action'), $form.serializeArray())
.done(function (json) {
json = json || {};
// In case of success, we redirect to the provided URL or the same page.
if (json.success) {
location = json.redirect || location.href;
} else if (json.errors) {
var errorSummary = getValidationSummaryErrors($form);
var items = $.map(json.errors, function (error) {
return '<li>' + error + '</li>';
}).join('');
var ul = errorSummary
.find('ul')
.empty()
.append(items);
}
});
}
// Prevent the normal behavior since we opened the dialog
e.preventDefault();
};
var loadAndShowDialog = function (id, link, url) {
var separator = url.indexOf('?') >= 0 ? '&' : '?';
// Save an empty jQuery in our cache for now.
dialogs[id] = $();
// Load the dialog with the content=1 QueryString in order to get a PartialView
$.get(url + separator + 'content=1')
.done(function (content) {
dialogs[id] = $('<div class="modal-popup">' + content + '</div>')
.hide() // Hide the dialog for now so we prevent flicker
.appendTo(document.body)
.filter('div') // Filter for the div tag only, script tags could surface
.dialog({ // Create the jQuery UI dialog
title: link.data('dialog-title'),
modal: true,
resizable: true,
draggable: true,
width: link.data('dialog-width') || 300
})
.find('form') // Attach logic on forms
.submit(formSubmitHandler)
.end();
});
};
// List of link ids to have an ajax dialog
var links = ['logonLink', 'registerLink'];
$.each(links, function (i, id) {
$('#' + id).click(function (e) {
var link = $(this),
url = link.attr('href');
if (!dialogs[id]) {
loadAndShowDialog(id, link, url);
} else {
dialogs[id].dialog('open');
}
// Prevent the normal behavior since we use a dialog
e.preventDefault();
});
});
});

knockout validation using breeze utility?

Has anyone written a utility that will convert Breeze metadata (captured from entity framework data attributes) into knockout validation extensions (using knockout.validation)?
I have made an function that reads the metadata from an entity and adds validation rules.
app.domain.indicador = (function () {
"use strict";
var constructor = function () {...}
var initializer = function indicadorInitializer(entity) {
var entityType = entity.entityType;
if (entityType) {
console.log(entityType);
for (var i = 0; i < entityType.dataProperties.length; i++) {
var property = entityType.dataProperties[i];
console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
if (!property.isNullable) {
propertyObject.extend({ required: true });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
}
for (var i = 0; i < entityType.foreignKeyProperties.length; i++) {
var property = entityType.foreignKeyProperties[i];
console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
if (!property.isNullable) {
propertyObject.extend({ required: true });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
//Bussines rule
propertyObject.extend({ notEqual: 0 });
}
}
};
return {
constructor: constructor,
initializer: initializer
};
})();
I use the function as initializer:
store.registerEntityTypeCtor("Indicador", domain.indicador.constructor, domain.indicador.initializer);
It's just a start but for the time is useful for me.
Update:
I changed the way I add validation. I share it here in case it is useful to someone:
Helper object:
app.validatorHelper = (function (breeze) {
var foreignKeyInvalidValue = 0;
function addDataTypeRules(dataType, property) {
switch (dataType) {
case breeze.DataType.DateTime:
//TODO: implement my function to validate dates. This validator is too permissive
property.extend({ date: true });
break;
case breeze.DataType.Int64:
case breeze.DataType.Int32:
case breeze.DataType.Int16:
//it's needed to accept negative numbers because of the autogenerated keys
property.extend({ signedDigit: true });
break;
case breeze.DataType.Decimal:
case breeze.DataType.Double:
case breeze.DataType.Single:
property.extend({ number: true });
break;
}
};
function addValidationRules(entity) {
var entityType = entity.entityType;
if (entityType) {
for (var i = 0; i < entityType.dataProperties.length; i++) {
var property = entityType.dataProperties[i];
//console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
addDataTypeRules(property.dataType, propertyObject);
if (!property.isNullable) {
propertyObject.extend({ required: true });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
}
for (var i = 0; i < entityType.foreignKeyProperties.length; i++) {
var property = entityType.foreignKeyProperties[i];
//console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
addDataTypeRules(property.dataType, propertyObject);
if (!property.isNullable) {
propertyObject.extend({ required: true });
//Bussiness Rule: 0 is not allowed for required foreign keys
propertyObject.extend({ notEqual: foreignKeyInvalidValue });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
}
}
};
return {
addValidationRules: addValidationRules
};
})(breeze);
The custom validator:
(function (ko) {
ko.validation.rules['signedDigit'] = {
validator: function (value, validate) {
if (!validate) return true;
return ko.validation.utils.isEmptyVal(value) || (validate && /^-?\d+$/.test(value));
},
message: 'Please enter a digit'
};
ko.validation.registerExtenders();
})(ko);
Using the helper at the initializer:
app.domain.valorIndicador = (function (vHelper) {
"use strict";
var constructor = function () {
};
var initializer = function indicadorInitializer(entity) {
vHelper.addValidationRules(entity);
};
return {
constructor: constructor,
initializer: initializer
};
})(app.validatorHelper);
And setting the initializer:
store.registerEntityTypeCtor("ValorIndicador", domain.valorIndicador.constructor, domain.valorIndicador.initializer);
A simple way to bind validation errors from breezejs using knockout.
We can subscribe to validationErrorsChanged event from the entityAspect:
function subscribeValidation() {
return self.entity().entityAspect.validationErrorsChanged.subscribe(function (validationChangeArgs) {
validationChangeArgs.added.forEach(function (item) { addError(item); });
validationChangeArgs.removed.forEach(function (item) { self.validationErrors.remove(item); });
});
}
this.hasError = function (propertyName) {
var array = self.validationErrors();
var match = array.filter(function (item) {
return item.propertyName == propertyName;
});
if (match.length > 0) {
return true;
} else return false;
};
function addError(item) {
self.validationErrors.remove(function (i) {
return i.propertyName == item.propertyName;
});
self.validationErrors.push(item);
}
Finally we can bind to the messages on the UI (I'm using Twitter boostrap css classes)
<div class="control-group" data-bind="css: { 'error': hasError('Nome') }">
<label class="control-label">Nome</label>
<div class="controls">
<input type="text" class="input-xxlarge" data-bind="value: model().Nome">
<span class="help-inline" data-bind="text: getErrorMessage('Nome')"></span>
</div>
</div>
See the full gist here
I've searched this before as I started using breeze with knockout and then I had the exact same question about how to validate stuff, and how to show validation inline.
Considering that breeze already has validation built in, I decided to write a custom Knockout Binding to show the validation result every time the observable value changes and it was quite easy afterall:
Here's the custom binding:
ko.bindingHandlers.breezeValidate = {
init: function (element, valueAccessor, allBindingsAccessor, context) {
var isOk = context.entityAspect.validateProperty(valueAccessor());
var errors = context.entityAspect.getValidationErrors(valueAccessor());
var message = "";
if (errors.length > 0)
message = errors[0].errorMessage;
$(element).html(message);
},
//update the control when the view model changes
update: function (element, valueAccessor, allBindingsAccessor, context) {
debugger;
this.init(element, valueAccessor, allBindingsAccessor, context)
}
};
And the usage is like this:
<span data-bind="text: Name"></span>
<span data-bind="breezeValidate: 'Name'"></span>
This works because of this line:
var isOk = context.entityAspect.validateProperty(valueAccessor());
When breeze is requested to validate the property it ends up calling the observable and it gets registered by knockout, so every time it is changed, this binding will be invoked again and the error message will be updated accordingly.
I'm just showing the first validation message, but of course you can iterate thru all of them and even add a different styling to the element.
Hope this helps!!
Not sure why people would want to use ko.validation - it just replicates the processing breeze's client side is doing anyway. And given the breeze developers hints that validation will get even more power soon, why bother.
So I started with Thiago Oliveira's great work. But I wanted to have the bare minimum of markup. By assuming the use of bootstrap classes & defaulting the validation property name from the previous element I could simplify most markup additions to:
<span class="help-inline" data-bind="breezeValidation: null"></span>
Win!
My ko.bindingHandler:
//Highlight field in red & show first validation message
//
//Outputs first validation message for 'propertyName' or if null: previous controls value binding
//Needs ancestor with 'control-group' class to set class 'error' for Bootstrap error display
//
//Example:
//<td class="control-group">
// <input class="input-block-level text-right" data-bind="value: id" />
// <span class="help-inline" data-bind="breezeValidation: null"></span>
//</td>
//
//Does not and cannot validate keys that already exist in cache. knockout write calls breeze which throws uncaught error
ko.bindingHandlers.breezeValidation = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
var $msgElement = $(element);
var entity = viewModel;
var propName = valueAccessor();
if (propName === null) {
// $element.prev().data("bind") = "value: itemType"
var prevBinds = $msgElement.prev().data("bind");
if (!prevBinds) {
$msgElement.text("Could not find prev elements binding value.");
return;
}
var bindPhrases = prevBinds.split(/,/);
for (var i = 0, j = bindPhrases.length; i < j; i++) {
var bindPhrase = bindPhrases[i];
if (utility.stringStartsWith(bindPhrase, 'value: ')) {
propName = bindPhrase.substr(7);
break;
}
}
}
if (!propName) {
$msgElement.text("Could not find this or prev elements binding value.");
return;
}
//var $groupElement = $msgElement.parent();
var $groupElement = $msgElement.closest(".control-group");
if (!$groupElement.hasClass("control-group")) {
$msgElement.text("Could not find parent with 'control-group' class.");
return;
}
onValidationChange(); //fire immediately (especially for added)
//... and anytime validationErrors are changed fire onValidationChnange
entity.entityAspect.validationErrorsChanged.subscribe(onValidationChange);
element.onchange = function () {
//Should never have updates pushed from validation msgElement
$msgElement.text("readonly error");
};
function onValidationChange() {
var errors = entity.entityAspect.getValidationErrors(propName);
var message = "";
if (errors.length > 0) {
message = errors[0].errorMessage;
}
if (message) {
$groupElement.addClass('error');
}
else {
$groupElement.removeClass('error');
}
$msgElement.text(message);
}
}
//Not interested in changes to valueAccessor - it is only the fieldName.
//update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
};
Example view simple implicit property usage:
<div class="control-group">
<label class="control-label" for="editStatusNote">Status note:</label>
<div class="controls">
<input id="editStatusNote" type="text" data-bind="value: statusNote" />
<span class="help-inline" data-bind="breezeValidation: null"></span>
</div>
</div>
Example view explicit property usage:
<div class="control-group">
<label class="control-label" for="editAmount">Amount:</label>
<div class="controls">
<div class="input-prepend">
<span class="add-on">$</span>
<input id="editAmount" class="input-small" type="text" data-bind="value: amount" />
</div>
<span class="help-inline" data-bind="breezeValidation: 'amount'"></span>
</div>
</div>
I updated breezeValidation to Bootstrap 3 and improved with multipath property support.
ko.bindingHandlers.breezeValidation = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
var $msgElement = $(element);
var entity = viewModel;
var propName = valueAccessor();
if (propName === null) {
// $element.prev().data("bind") = "value: itemType"
var prevBinds = $msgElement.prev().data("bind");
if (!prevBinds) {
$msgElement.text("Could not find prev elements binding value.");
return;
}
var bindPhrases = prevBinds.split(/,/);
for (var i = 0, j = bindPhrases.length; i < j; i++) {
var bindPhrase = bindPhrases[i];
if (bindPhrase.substr(0, 7) == 'value: ') {
propName = bindPhrase.substr(7);
entity = ko.utils.unwrapObservable(entity);
var propPath = propName.replace(/[()]/g, "").split('.'), i = 0;
var tempProp = entity[propPath[i]], links = propPath.length;
i++;
while (ko.utils.unwrapObservable(tempProp) && i < links) {
entity = ko.utils.unwrapObservable(tempProp);
tempProp = entity[propName = propPath[i]];
i++;
}
break;
}
}
}
if (!propName) {
$msgElement.text("Could not find this or prev elements binding value.");
return;
}
//var $groupElement = $msgElement.parent();
var $groupElement = $msgElement.closest(".form-group");
if (!$groupElement.hasClass("form-group")) {
$msgElement.text("Could not find parent with 'form-group' class.");
return;
}
onValidationChange(); //fire immediately (especially for added)
//... and anytime validationErrors are changed fire onValidationChnange
entity.entityAspect.validationErrorsChanged.subscribe(onValidationChange);
element.onchange = function () {
//Should never have updates pushed from validation msgElement
$msgElement.text("readonly error");
};
function onValidationChange() {
var errors = entity.entityAspect.getValidationErrors(propName);
var message = "";
if (errors.length > 0) {
message = errors[0].errorMessage;
}
if (message) {
$groupElement.addClass('has-error');
}
else {
$groupElement.removeClass('has-error');
}
$msgElement.text(message);
}
}
//Not interested in changes to valueAccessor - it is only the fieldName.
//update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
};
Knockout validator can use breeze validation as a whole:
function addKoValidationRules(entity) {
if (entity.koValidationRulesAdded) {
return;
}
entity.entityType.dataProperties.forEach(function (property) {
entity[property.name].extend({
validation: {
validator: function () {
// manual validation ensures subscription to observables which current field depends on
// entity is added to context for retrieving other properties in custom validators
entity.entityAspect.validateProperty(property.name, { entity: entity });
var errors = entity.entityAspect.getValidationErrors(property.name);
if (!errors.length) {
return true;
}
this.message = errors[0].errorMessage;
return false;
},
message: ''
}
});
});
entity.koValidationRulesAdded = true;
}

ajax add_endRequest never fires, on iPad only

I have some asp code in which I have a set of Telerik grids on separate jQueryUI tabs, and I am lazy-loading the grid data so that the grids only bind to live data if you actually view the tab that contains them. The rebind causes an ajax postback, and I have added an endRequest handler to re-apply the jQueryUI formatting once the request returns. This is working in Firefox, Chrome, Safari, and IE. But on the iPad the endRequest handler never fires. Any suggestions on how to troubleshoot this?
My code is as follows:
<script language="javascript" type="text/javascript">
(function ($, Sys) {
function setUpEmsDashboard() {
$('#emsDashboard').dnnTabs().dnnPanels();
$('#dInvoiceLink').click(function () {
lazyLoadOutstandingInvoicesGrid();
});
if ($('#dInvoice').is(':visible')) {
lazyLoadOutstandingInvoicesGrid();
}
$('#dCountsForStaffLink').click(function () {
lazyLoadCountsForStaffGrids();
});
if ($('#dCountsForStaff').is(':visible')) {
lazyLoadCountsForStaffGrids();
}
}
$(document).ready(function () {
setUpEmsDashboard();
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function () {
setUpEmsDashboard();
});
});
} (jQuery, window.Sys));
</script>
<telerik:RadCodeBlock ID="RadCodeBlock1" runat="server">
<script language="javascript" type="text/javascript">
function lazyLoadOutstandingInvoicesGrid() {
var grid = $find("<%=OutstandingInvoicesGrid.ClientID%>");
var masterTableView = grid.get_masterTableView();
var name = masterTableView.get_name();
if (name == 'Temp Data') {
masterTableView.rebind();
}
return true;
}
function lazyLoadCountsForStaffGrids() {
var countsBySalesRegionGrid = $find("<%=CountsBySalesRegionGrid.ClientID%>");
var cbsrMasterTableView = countsBySalesRegionGrid.get_masterTableView();
var cbsrName = cbsrMasterTableView.get_name();
if (cbsrName == 'Temp Data') {
cbsrMasterTableView.rebind();
return true;
}
var countsBySupplierTypeGrid = $find("<%=CountsBySupplierTypeGrid.ClientID%>");
var cbstMasterTableView = countsBySupplierTypeGrid.get_masterTableView();
var cbstName = cbstMasterTableView.get_name();
if (cbstName == 'Temp Data') {
cbstMasterTableView.rebind();
return true;
}
var countsByCategoryGrid = $find("<%=CountsByCategoryGrid.ClientID%>");
var cbcMasterTableView = countsByCategoryGrid.get_masterTableView();
var cbcName = cbcMasterTableView.get_name();
if (cbcName == 'Temp Data') {
cbcMasterTableView.rebind();
}
return true;
}
</script>
</telerik:RadCodeBlock>
Never mind, I found the issue. What I had was a race condition where for some browsers in some circumstances, the grid objects were null.
I changed:
function lazyLoadOutstandingInvoicesGrid() {
var grid = $find("<%=OutstandingInvoicesGrid.ClientID%>");
var masterTableView = grid.get_masterTableView();
var name = masterTableView.get_name();
if (name == 'Temp Data') {
masterTableView.rebind();
}
return true;
}
to:
function lazyLoadOutstandingInvoicesGrid() {
var grid = $find("<%=OutstandingInvoicesGrid.ClientID%>");
if (typeof (grid) !== 'undefined' && grid != null) {
var masterTableView = grid.get_masterTableView();
var name = masterTableView.get_name();
if (name == 'Temp Data') {
masterTableView.rebind();
}
return true;
}
}
...and made similar changes to the other function. That prevented the object reference error that had been silently causing the rest of the main function to fail. Now it works consistently.

Resources