Sunday 18 August 2013

Adding Validation Using Bootstrap Popover

I noticed an article by Damien Edwards called Updating the ASP.NET validator controls to change invalid control’s CSS class on non-JavaScript clients this got me thinking could I create an Control Adapter that would work for ALL Validators and would take advantage of the Bootstrap Tooltip or Popover JavaScript effects, Figure 1 is what I came up with.

Bootstrap Tooltip in action

Figure 1- Bootstrap Popover in action

So how did I do it, well I’m not entirely sure this is good practice, I’m using a Control Adapter to get the the Pre-Render event and then I get the the control as a BaseValidator get access to the ControlToValidate the about 30 lines of code and we have the above for any Validator.

protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);

var validator = Control as BaseValidator;
var controlType = Control.GetType().ToString();
var stringType = controlType.Substring(controlType.LastIndexOf(".") + 1, controlType.Length - controlType.LastIndexOf(".") - 1);

// build unique CCS class per validator type
var validatorError = String.Format("{0}-{1}", stringType, ERROR_CLASS);
var controlToValidate = Control.NamingContainer.FindControl(validator.ControlToValidate) as WebControl;
if (controlToValidate != null)
{
if (validator.IsValid)
{
// remove validator class
var className = String.Join(" ", controlToValidate.CssClass.Split(' ').Where(c => !c.Equals(validatorError)).ToArray());
controlToValidate.CssClass = className;
}
else
{
// add validator class
if (controlToValidate.CssClass.Split(' ').FirstOrDefault(c => c.Equals(validatorError)) == null)
controlToValidate.CssClass = String.IsNullOrEmpty(controlToValidate.CssClass)
? validatorError
: String.Format("{0} {1}", controlToValidate.CssClass, validatorError);

// add tooltip
controlToValidate.Attributes.Add("data-placement", "right");
controlToValidate.Attributes.Add("data-toggle", "popover");
controlToValidate.Attributes.Add("data-trigger", "hover");
controlToValidate.Attributes.Add("data-delay", "500");

// add title
controlToValidate.Attributes.Add("data-original-title", "Error!");
//TODO: add append errors to tooltip
controlToValidate.Attributes.Add("data-content", validator.ErrorMessage);

//$(document).ready(function () { $("#test").tooltip(); });
var datePickerScript = String.Format("$(document).ready(function () {{ $('#{0}').popover(); }});\n", controlToValidate.ClientID);
Control.Page.AddStartupClientScript(controlToValidate.ClientID, datePickerScript);
}
}
}

Listing 1 – Control Adapter OnPreRender

This piece of code does it all it finds the control we are validating and then add the CSS class or removes it depending on it being valid or not.

.inner-glow (@radius: 3px, @color: #f3ffa7)
{
-webkit-box-shadow: 0 0 @radius @radius @color inset !important;
box-shadow: 0 0 @radius @radius @color inset !important;
}

.required
{
.inner-glow;
}

.RequiredFieldValidator-validation-error,
.RegularExpressionValidator-validation-error,
.CompareValidator-validation-error,
.CustomValidator-validation-error,
.RangeValidator-validation-error,
.DynamicValidator-validation-error
{
.inner-glow(2px, fadeout(#ff0000, 50%));
border: 1px solid fadeout(#ff0000, 40%) !important;
}

Listing 2 – LESS Classes

I did the the styling using LESS as it’s available readily now with Mads Kristensen’s Web Essentials for Visual Studios 2012 and 2013.

Note: I think in the long term I want to do what Damien Edwards said inn his post and create an updated Validator for each of the main validators and also do a custom Popover that can be styles independently of the Bootstrap one.

Download

As usual you can get the code from the Project on Bootstrap Friendly Control Adaptors and on my SkyDrive

No comments: