Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Client-Side Validation with MonoRail

Like ASP.NET, MonoRail supports performing validation on both the server-side and the client-side.  Performing validation on the server-side is clean and keeps the validation logic with the domain objects that they validate.  The Castle Validator component is the way to go when doing so.  But there are certainly arguments to be made for performing client-side validation instead.  Validation on the client-side does not require a post-back, keeps domain objects clean and allows you to easily maintain validation logic within the view itself.  This post isn't to convince you to use one method over the other (as I see both appropriate for different scenarios), but to demonstrate how to:

  • Implement client-side validation with MonoRail/Brail using the ValidationHelper which is built upon fValidate.
  • Display consolidated error messages in a custom, validation summary at the top of the page.
  • Keep all validation infrastructure within the layout (i.e. master page) and out of the view.

If you're not familiar with it, fValidate is a JavaScript library for performing client-side validation.  Although not as powerful as Peter Blum (which doesn't play nicely with MonoRail), fValidate holds its own with a wide variety of built in validation capabilities including the ability to compare two inputs to each other along with other basic validation logic.  Prototype validation is another option...but that's for another post.  So then, on with validating...

To begin with, let's look at the complete view, MyView.brail, which reflects the very essence of simplicity (*cough* when compared with a typical ASP.NET WebForm):

<form method="post" action="SomeAction" onsubmit="return Validate(this);"> Username: ${Form.TextField('username', {'validators':'blank', 'emsg':'Username must be supplied'})} <br> Password: <input type="password" name="password" validators="length|8" emsg="Password must be at least 8 chars long"> <br> <input type="submit" value="Submit"></input> </form>

The entire view consists only of a form tag, a couple of input boxes (one defined via brail and one explicitly) along with a submit button.  The validators attribute defines which validators should be applied to the respective inputs and the emsg defines the error message that should be shown if the user does not complete the form correctly.  The call to Validate is what invokes the validation infrastructure that's been tucked away in the layout, bound to this view via the controller with the [Layout("MyLayout")] attribute.

Moving on, the view's layout, MyLayout.brail, holds the more interesting bits of this process, as demonstrated below:

... <head> ${ValidationHelper.InstallScripts() .Replace("/MyApp/MonoRail/Files/ValidateConfig.rails?RC3_0006", "/MyApp/Includes/fValidate/fValidate.config.js")} <script language="javascript"> function Validate(formToValidate) { ResetFormValidation(); var isFormValid = validateForm(formToValidate, false, true, false, true, 4); if (! isFormValid) { document.getElementById('errorSummary').style.visibility = ""; document.getElementById('errorSummary').style.display = ""; } return isFormValid; } function ResetFormValidation() { document.getElementById('errors').innerHTML = ""; document.getElementById('errorSummary').style.visibility = "hidden"; document.getElementById('errorSummary').style.display = "none"; } </script> </head> <body> <span id="errorSummary" style="visibility:hidden; display:none;"> <table cellpadding="0" cellspacing="0"> <tr> <td style="width:100%" align="left"> <div class="MyStyledErrors"> Please note the following: <ul id='errors'></ul> </div> </td> </tr> </table> </span> ...

The layout code performs the following:

  1. InstallScripts registers the fValidate scripts embedded within the MonoRail framework.  There's nothing magical about the registration; it simply spits out <script> registration tags for the appropriate JavaScript includes.  Notice that I've intercepted the return value from InstallScripts and replaced the configuration script with my own configuration settings.  MonoRail's config file for fValidate varies subtley from the 5.01b version of fValidate (not English) so it's important to grab and customize MonoRail's version instead of the one in the 5.01b build.  To grab MonoRail's version, browse to /YourApp/MonoRail/Files/ValidateConfig.rails?RC3_0006.  (This may vary based on your version of MonoRail; to verify, leave off the replace method and it'll spit out the location of the default configuration settings.)  Simply save the configuration js file locally, throw it into an includes folder, make any desired modifications, and replace the inclusion of it as I demonstrated.  The only modification to the js configuration that I've made is to set this.clearEvent = null; in lieu of the 'change' value set by default. A quick example will help explain why I've done this. Assume you have two inputs and that both are required and left blank. When the user submits the form, two error messages will be displayed. If you make a modification to one of the inputs and tab out of the input, then the respective error message will go away. It will go away even if the text in the input still violates one of the validators. Essentially, it makes the assumption that the user has corrected the error and only checks it again when the form is resubmitted. I don't like this for the fact that having the error message automatically disappear implies to the user that the problem has been resolved when in fact it's still unknown until the form is resubmitted. By setting this.clearEvent = null;, you're disabling the onchange event handling and the error messages will not change until the form is resubmitted. You can certainly vary this behavior according to your needs.
  2. Validate is a custom method, not from fValidate, which does three things.  First, it resets the validation summary by removing any error messages that are currently displayed and then hides the validation summary.  If you don't hide the error messages and the user resubmits the form, fValidate will attempt to re-add the same errors with the same IDs leading to JavaScript errors and having the form submitted without being validated a second time.  Second, it actually validates the form with a call to fValidate's validateForm method.  If validation errors are found, this is the point wherein the error messages are injected into the validation summary.  (Parameters to this method are discussed in detail here.)  Finally, if any errors were encountered, the method unhides the validation summary.
  3. The custom errorSummary span describes how the validation summary should be displayed.  The unordered error list itself is empty and is populated automatically by fValidate.  Its ID of errors is not arbitrary and is necessary in order for fValidate to know where to show the error messages.  (This can be modified in the js configuration file.)

This setup enables client-side validation in MonoRail with a fully customized validation summary which appears when validation errors exist.  There are certainly other ways to do this and, alternatively, having the errors appear right next to the inputs they describe requires a bit less work than what's described above.  But once everything is in place and tucked away in the layout, the views become trivially simple while still enabling a nice validation summary at the top of the page.

Billy McCafferty


Posted 08-02-2007 5:40 PM by Billy McCafferty
Filed under:

[Advertisement]

Comments

Dan Goldstein wrote re: Client-Side Validation with MonoRail
on 08-03-2007 9:01 AM

MonoRail has client-side validation that's a bit easier to use. Create a class with the properties you want and mark the properties with validator attributes like ValidateNonEmpty. In the controller, do something like PropertyBag["objtype"] = typeof(NewClass) and then change the name of the fields so they start with "obj.". Put FormHelper.InstallScripts in the layout and use FormHelper.FormTag and FormHelper.EndFormTag. You could also use the DataBind attribute to collect the form results directly into an instance of the class. It's quite neat.

Billy McCafferty wrote re: Client-Side Validation with MonoRail
on 08-03-2007 10:02 AM

Thanks Dan.  I'll readily admit that I'm still getting my feet wet with MonoRail.  Could you comment on when it would be appropriate to use the various approaches such as fValidate, Prototype and within the domain objects?  Do you feel that one of these has become the primary "standard" way of doing validation in MonoRail?

Henrik wrote re: Client-Side Validation with MonoRail
on 01-08-2008 7:29 AM

If your domain objects are complex enough they reside in the core and not in the presentation layer leaving the presentation layer with data transfer objects which you create on a per-page basis with custom Castle.Components.Validators attributes on properties and let monorail build validation...? When to use manual validation; perhaps when you're doing more complex types of validation?

(It would be cool if we could add an attribute to the DTOs in castle's validation similar to [SelfValidation] in MSP&P which then would automatically translate the CLR code in there to javascript! :) To facilitate for more advanced validation scenarios!)

Btw... I can't comment here if I have JS turned on, because the captcha text field validates as not-filled-out with ASP.Net validator...

Billy McCafferty wrote re: Client-Side Validation with MonoRail
on 01-08-2008 11:36 AM

I believe it's a personal preference with respect to where you put the validation.  On my current, good-sized project, all validation is in the presentation layer and this has worked quite well for our needs.  But on our next larger project, I'd like to put validation attributes in the domain to give that approach a good try as well.

Although separated from the domain objects that they validate, having validation in the presentation layer is easy to maintain and is very responsive.

Six of one, half dozen of another?  I'm not sure yet. ;)

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)