Skip to main content

4 posts tagged with "jquery validation"

View All Tags

jQuery Validation Globalize hits 1.0

This is just a quick post - the tl;dr is this: jQuery Validation Globalize has been ported to Globalize 1.x. Yay! In one of those twists of fate I'm not actually using this plugin in my day job anymore but I thought it might be useful to other people. So here you go. You can read more about this plugin in an older post and you can see a demo of it in action here.

The code did not change drastically - essentially it was just a question of swapping parseFloat for parseNumber and parseDate for a slightly different parseDate. So, we went from this:

(function ($, Globalize) {
// Clone original methods we want to call into
var originalMethods = {
min: $.validator.methods.min,
max: $.validator.methods.max,
range: $.validator.methods.range
};
// Tell the validator that we want numbers parsed using Globalize
$.validator.methods.number = function (value, element) {
var val = Globalize.parseFloat(value);
return this.optional(element) || ($.isNumeric(val));
};
// Tell the validator that we want dates parsed using Globalize
$.validator.methods.date = function (value, element) {
var val = Globalize.parseDate(value);
return this.optional(element) || (val instanceof Date);
};
// Tell the validator that we want numbers parsed using Globalize,
// then call into original implementation with parsed value
$.validator.methods.min = function (value, element, param) {
var val = Globalize.parseFloat(value);
return originalMethods.min.call(this, val, element, param);
};
$.validator.methods.max = function (value, element, param) {
var val = Globalize.parseFloat(value);
return originalMethods.max.call(this, val, element, param);
};
$.validator.methods.range = function (value, element, param) {
var val = Globalize.parseFloat(value);
return originalMethods.range.call(this, val, element, param);
};
}(jQuery, Globalize));

To this:

(function ($, Globalize) {
// Clone original methods we want to call into
var originalMethods = {
min: $.validator.methods.min,
max: $.validator.methods.max,
range: $.validator.methods.range
};
// Globalize options - initially just the date format used for parsing
// Users can customise this to suit them
$.validator.methods.dateGlobalizeOptions = { dateParseFormat: { skeleton: "yMd" } };
// Tell the validator that we want numbers parsed using Globalize
$.validator.methods.number = function (value, element) {
var val = Globalize.parseNumber(value);
return this.optional(element) || ($.isNumeric(val));
};
// Tell the validator that we want dates parsed using Globalize
$.validator.methods.date = function (value, element) {
var val = Globalize.parseDate(value, $.validator.methods.dateGlobalizeOptions.dateParseFormat);
return this.optional(element) || (val instanceof Date);
};
// Tell the validator that we want numbers parsed using Globalize,
// then call into original implementation with parsed value
$.validator.methods.min = function (value, element, param) {
var val = Globalize.parseNumber(value);
return originalMethods.min.call(this, val, element, param);
};
$.validator.methods.max = function (value, element, param) {
var val = Globalize.parseNumber(value);
return originalMethods.max.call(this, val, element, param);
};
$.validator.methods.range = function (value, element, param) {
var val = Globalize.parseNumber(value);
return originalMethods.range.call(this, val, element, param);
};
}(jQuery, Globalize));

All of which is pretty self-explanatory. The only thing I'd like to draw out is that Globalize 0.1.x didn't force you to specify a date parsing format and, as I recall, would attempt various methods of parsing. For that reason jQuery Validation Globalize 1.0 exposes a $.validator.methods.dateGlobalizeOptions which allows you to specify the data parsing format you want to use. This means, should you be using a different format than the out of the box one then you can tweak it like so:

$.validator.methods.dateGlobalizeOptions.dateParseFormat = // your data parsing format goes here...

Theoretically, this functionality could be tweaked to allow the user to specify multiple possible date parsing formats to attempt. I'm not certain if that's a good idea though, so it remains unimplemented for now.

Migrating from jquery.validate.unobtrusive.js to jQuery.Validation.Unobtrusive.Native

So, you're looking at jQuery.Validation.Unobtrusive.Native. You're thinking to yourself "Yeah, I'd really like to use the native unobtrusive support in jQuery Validation. But I've already got this app which is using jquery.validate.unobtrusive.js - actually how easy is switching over?" Well I'm here to tell you that it's pretty straightforward - here's a walkthrough of how it might be done.

I need something to migrate#

So let's File > New Project ourselves a new MVC 4 application using the Internet Application template. I've picked this template as I know it ships with account registration / login screens in place which make use of jquery.validate.unobtrusive.js. To demo this just run the project, click the "Log in" link and then click the "Log in" button - you should see something like this:

What you've just witnessed is jquery.validate.unobtrusive.js doing its thing. Both the UserName and Password properties on the LoginModel are decorated with the Required data annotation which, in the above scenario, causes the validation to be triggered on the client thanks to MVC rendering data attributes in the HTML which jquery.validate.unobtrusive.js picks up on. The question is, how can we take the log in screen above and migrate it across to to using jQuery.Validation.Unobtrusive.Native?

Hit me up NuGet!#

Time to dive into NuGet and install jQuery.Validation.Unobtrusive.Native. We'll install the MVC 4 version using this command:

PM> Install-Package jQuery.Validation.Unobtrusive.Native.MVC4

What has this done to my project? Well 2 things

  1. It's upgraded jQuery Validation (jquery.validate.js) from v1.10.0 (the version that is currently part of the MVC 4 template) to v1.11.1 (the latest and greatest jQuery Validation as of the time of writing)
  2. It's added a reference to the jQuery.Validation.Unobtrusive.Native.MVC4 assembly, like so:

In case you were wondering, doing this hasn't broken the existing jquery.validate.unobtrusive.js - if you head back to the Log in screen you'll still see the same behaviour as before.

Migrating...#

We need to switch our TextBox and Password helpers over to using jQuery.Validation.Unobtrusive.Native, which we achieve by simply passing a second argument of true to useNativeUnobtrusiveAttributes. So we go from this:

// ...
@Html.TextBoxFor(m => m.UserName)
// ...
@Html.PasswordFor(m => m.Password)
// ...

To this:

// ...
@Html.TextBoxFor(m => m.UserName, true)
// ...
@Html.PasswordFor(m => m.Password, true)
// ...

With these minor tweaks in place the natively supported jQuery Validation data attributes will be rendered into the textbox / password elements instead of the jquery.validate.unobtrusive.js ones.

Next lets do the JavaScript. If you take a look at the bottom of the Login.cshtml view you'll see this:

@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

Which renders the following scripts:

<script src="/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>

In our brave new world we're only going to need jquery.validate.js - so let's create ourselves a new bundle in BundleConfig.cs which only contains that single file:

bundles.Add(new ScriptBundle("~/bundles/jqueryvalnative")
.Include("~/Scripts/jquery.validate.js"));

To finish off our migrated screen we need to do 2 things. First we need to switch over the Login.cshtml view to only render the jquery.validate.js script (in the form of our new bundle). Secondly, the other thing that jquery.validate.unobtrusive.js did was to trigger validation for the current form. So we need to do that ourselves now. So our finished Scripts section looks like this:

@section Scripts {
@Scripts.Render("~/bundles/jqueryvalnative")
<script>
$("form").validate();
</script>
}

Which renders the following script:

<script src="/Scripts/jquery.validate.js"></script>
<script>
$("form").validate();
</script>

And, pretty much, that's it. If you run the app now and go to the Log in screen and try to log in without credentials you'll get this:

Which is functionally exactly the same as previously. The eagle eyed will notice some styling differences but that's all it comes down to really; style. And if you were so inclined you could easily style this up as you liked using CSS and the options you can pass to jQuery Validation (in fact a quick rummage through jquery.validate.unobtrusive.js should give you everything you need).

Rounding off#

Before I sign off I'd like to illustrate how little we've had to change the code to start using jQuery.Validation.Unobtrusive.Native. Just take a look at this code comparison:

As you see, it takes very little effort to migrate from one approach to the other. And it's *your* choice. If you want to have one screen that uses jQuery.Validation.Unobtrusive.Native and one screen that uses jquery.validation.unobtrusive.js then you can! Including jQuery.Validation.Unobtrusive.Native in your project gives you the option to use it. It doesn't force you to, you can do so as you need to and when you want to. It's down to you.

Using Bootstrap Tooltips to display jQuery Validation error messages

I love jQuery Validation. I was recently putting together a screen which had a lot of different bits of validation going on. And the default jQuery Validation approach of displaying the validation messages next to the element being validated wasn't working for me. That is to say, because of the amount of elements on the form, the appearance of validation messages was really making a mess of the presentation. So what to do?

Tooltips to the rescue!#

I was chatting to Marc Talary about this and he had the bright idea of using tooltips to display the error messages. Tooltips would allow the existing presentation of the form to remain as is whilst still displaying the messages to the users. Brilliant idea!

After a certain amount of fiddling I came up with a fairly solid mechanism for getting jQuery Validation to display error messages as tooltips which I'll share here. It's worth saying that for the application that Marc and I were working on we already had jQuery UI in place and so we decided to use the jQuery UI tooltip. This example will use the Bootstrap tooltip instead. As much as anything else this demonstrates that you could swap out the tooltip mechanism here with any of your choosing.

Beautiful isn't it? Now look at the source:

All the magic is in the JavaScript, specifically the showErrors function that's passed as an option to jQuery Validation. Enjoy!

Globalize and jQuery Validation

Update 05/10/2015#

If you're after a version of this that works with Globalize 1.x then take a look here.

Update 27/08/2013#

To make it easier for people to use the approach detailed in this post I have created a repository for jquery.validate.globalize.js on GitHub here.

This is also available as a nuget package here.

To see a good demo take a look here.

Background#

I've written before about a great little library called Globalize which makes locale specific number / date formatting simple within JavaScript. And I've just stumbled upon an old post written by Scott Hanselman about the business of Globalisation / Internationalisation / Localisation within ASP.NET. It's a great post and I recommend reading it (I'm using many of the approaches he discusses).

jQuery Global is dead... Long live Globalize!#

However, there's one tweak I would make to Scotts suggestions and that's to use Globalize in place of the jQuery Global plugin. The jQuery Global plugin has now effectively been reborn as Globalize (with no dependancy on jQuery). As far as I can tell jQuery Global is now disappearing from the web - certainly the link in Scotts post is dead now at least. I've ripped off been inspired by the "Globalized jQuery Unobtrusive Validation" section of Scotts article and made jquery.validate.globalize.js.

And for what it's worth jquery.validate.globalize.js applies equally to standard jQuery Validation as well as to jQuery Unobtrusive Validation. I say that as the above JavaScript is effectively a monkey patch to the number / date / range / min / max methods of jQuery.validate.js which forces these methods to use Globalize's parsing support instead.

Here's the JavaScript:

The above script does 2 things. Firstly it monkey patches jquery.validate.js to make use of Globalize.js number and date parsing in place of the defaults. Secondly it initialises Globalize to relevant current culture driven by the html lang property. So if the html tag looked like this:

<html lang="de-DE">
...
</html>

Then Globalize would be initialised with the "de-DE" culture assuming that culture was available and had been served up to the client. (By the way, the Globalize initialisation logic has only been placed in the code above to demonstrate that Globalize needs to be initialised to the culture. It's more likely that this initialisation step would sit elsewhere in a "proper" app.)

Wait, where's html lang getting set?#

In Scott's article he created a MetaAcceptLanguage helper to generate a META tag like this: &lt;meta name="accept-language" content="en-GB" /&gt; which he used to drive Globalizes specified culture.

Rather than generating a meta tag I've chosen to use the lang attribute of the html tag to specify the culture. I've chosen to do this as it's more in line with the W3C spec. But it should be noted this is just a different way of achieving exactly the same end.

So how's it getting set? Well, it's no great shakes; in my _Layout.cshtml file my html tag looks like this:

<html lang="@System.Globalization.CultureInfo.CurrentUICulture.Name">

And in my web.config I have following setting set:

<configuration>
<system.web>
<globalization culture="auto" uiCulture="auto" />
<!--- Other stuff.... -->
</system.web>
</configuration>

With both of these set this means I get &lt;html lang="de-DE"&gt; or &lt;html lang="en-GB"&gt; etc. depending on a users culture.

Serving up the right Globalize culture files#

In order that I send the correct Globalize culture to the client I've come up with this static class which provides the user with the relevant culture URL (falling back to the en-GB culture if it can't find one based your culture):

Putting it all together#

To make use of all of this together you'll need to have the html lang attribute set as described earlier and some scripts output in your layout page like this:

<script src="@Url.Content("~/Scripts/jquery.js")" type="text/javascript"></script>
<script src="@Url.Content(GlobalizeUrls.Globalize)" type="text/javascript"></script>
<script src="@Url.Content(GlobalizeUrls.GlobalizeCulture)" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/scripts/jquery.validate.globalize.js")" type="text/javascript"></script>
@* Only serve the following script if you need it: *@
<script src="@Url.Content("~/scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>

Which will render something like this:

<script src="/Scripts/jquery.js" type="text/javascript"></script>
<script src="/Scripts/globalize.js" type="text/javascript"></script>
<script src="/scripts/globalize/globalize.culture.en-GB.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.globalize.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js" type="text/javascript"></script>

This will load up jQuery, Globalize, your Globalize culture, jQuery Validate, jQuery Validates unobtrusive extensions (which you don't need if you're not using them) and the jQuery Validate Globalize script which will set up culture aware validation.

Finally and just to re-iterate, it's highly worthwhile to give Scott Hanselman's original article a look. Most all the ideas in here were taken wholesale from him!