Validation In Monorail

Link. August 16, 2007. Comments [2]. Posted in: Castle

I've been building a small application the last few days using Castle, including both Monorail and ActiveRecord, which I had not used before. It's been a very pleasant experience overall, as I dig a bit deeper into some of the features in Castle I had not used before. At the same time, it's been a bit frustrating as some of the documentation isn't quite up to date, or, more commonly, doesn't quite list all the necessary steps.

One of the aspects that gave me a bit of trouble getting to run was using Castle.Components.Validator to add validation to the application. Hammett has a nice introduction to the validation framework on this screencast, but even following this and the documentation I couldn't quite get it to work (I think part of the issue was that I use brail, while he uses nvelocity).

In this example, I'm using ActiveRecord for persistence so that will affect some of the code, so be aware of that. Here's exactly what worked for me.

Step 1: Reference Castle.Components.Validator.dll on your project where your model classes are defined. Where necessary, reference the Castle.Components.Validator namespace. Make sure you pick a recent copy of the Castle libraries from the build server :-).

Step 2: Add validation attributes to the properties of your model classes. Make sure you explicitly specify a proper error message on your attributes, because the default messages might not be as informative. Here's an example:

[Property(Length = 100, NotNull = true)]
[ValidateNonEmpty("Name cannot be empty")]
[ValidateIsUnique("Name must be unique")]
public string Name { 
   get { return _name; }
   set { _name = value; }
}

Step 3: Inherit your monorail controller from SmartDispatcherController so that the validation facilities are available. In my case, I'll use the ARSmartDispatcherController class so that I can use the nice ActiveRecord integration facilities as well:

public class ClientController : ARSmartDispatcherController { 
   // ...
}

Step 4: Use the [DataBiind] or [ARDataBind] attributes on your handler methods, and make sure you enable the Validate property:

public void Create(
   [ARDataBind("client", Validate=true)] Client client
) {
   //...
}

Step 5: Inside your handler code, check if the bound object contains error before persisting and if so redirect as appropriate if not. You can use the HasValidationError() method to check if the object triggered validation errors, and then call GetValidationSummary() to get the list of error messages.

if ( HasValidationError(client) ) {
   Flash["client"] = client;
   Flash["summary"] = GetErrorSummary(client);
   RedirectToReferrer();
} else {
   client.Create();
   RedirectToAction("list");
}

I'm currently wrapping GetErrorSummary() in my base controller class so that I can return a single string with all validation errors to avoid having to loop around in the view (I'm lazy):

protected string GetErrorSummaryMsg(object obj) {
   StringBuilder text = new StringBuilder();
   bool isFirst = true;
   foreach ( string str in GetErrorSummary(obj).ErrorMessages ) {
      if ( !isFirst )
         text.Append("<br/>");
      text.Append(str);
      isFirst = false;
   }
   return text.ToString();
}

So far you should already have server-side validation working. Now let's add client side validation:

Step 6: Include the necessary for client side validation to work; you'll want to do this in your layout view:

${AjaxHelper.InstallScripts()}
${Scriptaculous.InstallScripts()}
${FormHelper.InstallScripts()}

The last one, in particular, is pretty important; without it you'll later get bizarre JS errors like "Validation is not defined". This actually took me a while to get because it's the only one that Hammett doesn't explicitly include in his demo.

Step 7: Enable client-side validation in your form. For this, you need to add a couple of custom attributes to your form tag: immediate and useLabels and set them to true. Using FormHelper, here's what it would look like:

${FormHelper.FormTag("create.rails", {"immediate":'true', "useLabels":'true'})}
... ${FormHelper.EndFormTag()}

For the curious, this actually generates the following <form/> tag:

<form action='create.rails' method='post' id='form1' useLabels="true" >

The closing tag will also contain the validation script using whatever validation provider your selected (prototypes, by default), unless you disable validation using FormHelper.DisableValidation().

Step 8: Declare your field labels and input controls. Using FormHelper, again, this is easy:

<tr>
   <td>${FormHelper.LabelFor("client.Name", "Name:")}</td>
   <td>${FormHelper.TextField("client.Name")}</td>
</tr>

Step 9: Put the error messages from the server-side validation somewhere on your form:

<?brail if IsDefined("summary"): ?>
<tr>
   <td colspan='2' class='validator'>${summary}</td>
</tr>
<?brail end ?>

validation

And now, client side validation should be working :-) Notice that the client-side validation messages seem to be tagged with the "validation-advice" CSS class, so that makes them easy to style.



Wednesday, August 15, 2007 8:58:36 PM (SA Pacific Standard Time, UTC-05:00)
Monorail is such a relief from the pain that is ASP.Net. Now, instead of the horrid integrated ASP.Net validation controls you have attributes, inheritance, yet another tag soup language, inline loops, conditionals all over the place, terabytes of yet another javascript library, all this tightly wrapped in 9 steps to get the whole thing - hopefully - working.

It's almost as beautiful as the NIH/non ASP.Net rendering model used in CruiseControl.Net web dashboard. So simple and extensible that it takes two to three years to get some changes done (and the whole thing is dead slow, but cache is coming, soon, really).
yartz
Wednesday, August 15, 2007 9:04:34 PM (SA Pacific Standard Time, UTC-05:00)
@Yartz: I realize this sounds like a lot of steps, but in the end it turns out pretty easy once you get the hang of it. Notice, however, that a lot of the steps I outlines really have little to do with validation per-se; they are added to enable databinding and other stuff. Certainly, if you described the entire process for ASP.NET validators, it would also contain a lot of steps; it's not really just drag and drop the validator component and that's it.

Besides, I've personally have had horrendous experience with the built-in asp.net validator framework; so much that in one project I participated they ended up writing their own.
Comments are closed.

About

Tomas Restrepo is co-founder of devdeo. His interests include .NET, Connected Systems, PowerShell and, lately, dynamic programming languages. More...

email: tomas@winterdom.com
msn: tomasr@passport.com
twitter: tomas_restrepo

Technorati Profile

devdeo logo

View my profile on LinkedIn

MVP logo

Syndicate

Ads


Links

Categories

Statistics

Total Posts: 1006
This Year: 76
This Month: 7
This Week: 0
Comments: 771

Blogroll

Post Archive

Other

Copyright © 2002-2008, Tomas Restrepo.

Powered by: newtelligence dasBlog 1.9.7174.0

Sign In