MonoRail #4: Validation

This post was decided upon after watching Hammet‘s screencast on using attribute-based validation in MonoRail.

*NOTE: I do not agree with embedding validation into the domain model.  I would prefer a service handle this, but I’m utilizing the validation that is in the framework.  I’ve been skimming some of Bill McCafferty’s MonoRail posts and have seen him use items like fValidate, but haven’t had an opportunity to use them.  Anyone have any preferences?

Be aware that I’m using the latest trunk code: Get it here from the Castle MonoRail build server.

First, you need a reference to Castle.Components.Validator in your project:
image

THE MODEL:

Next, you need to add the necessary attributes to the domain entity’s properties:

   1: using Castle.Components.Validator;
   2:  
   3: namespace JasonMeridth.Models
   4: {
   5:     public class Contact
   6:     {
   7:         private string name;
   8:         private string email;
   9:         private string message;
  10:  
  11:         [ValidateNonEmpty("Name is a required field")]
  12:         public string Name
  13:         {
  14:             get { return name; }
  15:             set { name = value; }
  16:         }
  17:  
  18:         [ValidateNonEmpty("Email is a required field"), ValidateEmail("Email is not in proper format")]
  19:         public string Email
  20:         {
  21:             get { return email; }
  22:             set { email = value; }
  23:         }
  24:  
  25:         [ValidateNonEmpty("Message is a required field")]
  26:         public string Message
  27:         {
  28:             get { return message; }
  29:             set { message = value; }
  30:         }
  31:     }
  32: }

The ValidateNonEmpty inherits from the AbstractValidationAttribute abstract class and is self-explanatory.  The string following that is what error message will be shown on the view, if the validation fails.

Other validation attributes include:
Castle.ActiveRecord..::ValidateIsUniqueAttribute
ValidateCreditCardAttribute
ValidateDateAttribute
ValidateCollectionNotEmptyAttribute
ValidateDateTimeAttribute
ValidateDecimalAttribute
ValidateDoubleAttribute
ValidateEmailAttribute
ValidateGroupNotEmptyAttribute
ValidateIntegerAttribute
ValidateLengthAttribute
ValidateRangeAttribute
ValidateNotSameValueAttribute
ValidateRegExpAttribute
ValidateSameAsAttribute
ValidateNotSameAsAttribute
ValidateSetAttribute
ValidateSingleAttribute
ValidateNonEmptyAttribute

Notice on the Email property I also included the ValidateEmail attribute.  Finally I don’t have to write my email RegEx validation. :)

THE CONTROLLER:

   1: using System;
   2: using Castle.MonoRail.Framework;
   3: using JasonMeridth.Models;
   4:  
   5: namespace JasonMeridth.Controllers
   6: {
   7:     [Layout("default"), Rescue("generalerror")]
   8:     public class ContactController : SmartDispatcherController
   9:     {
  10:         public void ContactForm()
  11:         {
  12:             PropertyBag["contacttype"] = typeof (Contact);
  13:             RenderView("contactform");
  14:         }
  15:  
  16:         public void SendContactMessage([DataBind("contact", Validate=true)] Contact contact)
  17:         {
  18:             if (HasValidationError(contact))
  19:             {
  20:                 Flash["contact"] = contact;
  21:                 Flash["summary"] = GetErrorSummary(contact);
  22:                 RedirectToAction("contactform");
  23:             }
  24:  
  25:             PropertyBag["contact"] = contact;
  26:             RenderView("confirmation");
  27:         }
  28:     }
  29: }

Notice the DataBind attribute now has a Validate parameter.  This property sets a value indicating whether the target, in my case the Contact instance, should be validated during binding.

To see if a target fails or passes validation you use the following if statement:

   1: if (HasValidationError(contact))

If validation fails, Hammet suggests doing the following:

   1: Flash["contact"] = contact;
   2: Flash["summary"] = GetErrorSummary(contact);
   3: RedirectToAction("contactform");

Flash is a way to persist a transient value between requests. I’m using it here to receive the error information.  I add the GetErrorSummary(contact) information into the Flash with key “summary”.  Hammet also suggests adding the instance to the Flash so that a developer may use it to pre-populate fields that had inputted data.  Kind of like what we, ASP.NET developers get with ViewState.

If there is no error, I send the contact instance to the property bag and I render the confirmation view.

THE VIEW:

   1: $Form.FormTag("%{action='sendcontactmessage', immediate='true', useLabels='true'}")
   2:  
   3: #if($Flash.summary)
   4: <br />
   5:     #foreach($errormessage in $summary.ErrorMessages)
   6:         $errormessage<br />
   7:     #end
   8: <br />
   9: #end
  10:  
  11: <h2>Drop me a message!</h2>
  12:  
  13: <br />
  14: $Form.LabelFor("contact.name", "Name:")
  15: $Form.TextField("contact.name")
  16: <br />
  17:  
  18: <br />
  19: $Form.LabelFor("contact.email", "Email:")
  20: $Form.TextField("contact.email")
  21: <br />
  22:  
  23: <br />
  24: $Form.LabelFor("contact.message", "Message:")
  25: $Form.TextArea("contact.message", "%{cols='35', rows='6'}")
  26: <br />
  27:  
  28: <hr />
  29:  
  30: $Form.Submit("Send the message")
  31:  
  32: $Form.EndFormTag()

 1.I’m using the FormHelper syntax here so that databinding between the view and the controller is automatic.  How exactly that wires-up, I’m still researching the source (future update to this post**).  Don’t get me wrong, most of this was verbatim from Hammett’s screencast.

The FormHelper methods that I used (and links to the api documentation):
$Form.LabelFor - generates a label element
$Form.FormTag - generates a form tag
$Form.EndFormTag - generates an end form tag
$Form.TextField - generates a textbox, value is extracted from the target (first parameter in my case)
$Form.TextArea - generates a textarea, value is extracted from the target (first parameter in my case)
$Form.Submit - generates an input submit element

2.Notice the the if statement at the top regarding $Flash.summary.  What this does is if validation failed in the controller (remember we added items to Flash), then we iterate over the error messages and display them on the UI.

3. Notice the form tag:
$Form.FormTag(“%{action=’sendcontactmessage’, immediate=’true’, useLabels=’true’}”)

action – tells the form which action to run on it’s controller (notice it’s the same name as the action we worked on above)
immediate – tells the form to initiate validation as soon as the user leaves a field (this requires some wire-up of javascript – look below at #4)
useLabels – tells the form to display the errors using a label

4.The other view related thing you have to change/add is the following to the default.vm (wherever you have your head block):

   1: <head>
   2: <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
   3: <title>JasonMeridth 1.0</title>
   4: <meta name="keywords" content="Jason Meridth, San Antonio, Alamo Coders, Los Techies" />
   5: <meta name="description" content="The personal webpage of Jason Meridth" />
   6: <link rel="stylesheet" href="$siteRoot/Content/css/default.css" />
   7: $Ajax.InstallScripts()
   8: $Scriptaculous.InstallScripts()
   9: $FormHelper.InstallScripts()
  10: </head>

The key lines to pay attention to are:

   1: $Ajax.InstallScripts()
   2: $Scriptaculous.InstallScripts()
   3: $FormHelper.InstallScripts()

These lines cause client-side validation to work and wire-up properly. I’m still researching all details (update to this post coming**).

** – I know I have 2 sections in the post where I say I’m going to be researching and updating this post, please bare with me.  I’m honestly hoping that an experienced MonoRail developer may comment on this entry and educate me with real-world info.  No matter, I will get this information to everyone ASAP.

*** NOTE: The one issue that I had to work out is that my contact validation was bombing out in IE7.  I did some googling and found this post by Jeff Newsom about a known IE7 bug where if you place code into a non-block element (i.e., form) via innerHTML you may receive weird errors.  In Hammet’s screen cast you see that he is using <p> tags around his form elements.  Once I changed those to <br /> tags, I didn’t have any more errors.  I’m still researching better alternatives, but at least my contact form now works in IE6+. :)

NEXT POST: Sending an email and Authentication

Prior Posts:
Monorail #0:Controllers
Monorail #1:Reasons, Setup, and First Output
Monorail #2:Layouts and Rescues
Monorail #3:Unit Testing and ViewComponents

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jason Meridth

Continuously learning software developer trying to not let best be the enemy of better
This entry was posted in castle, monorail. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

9 Responses to MonoRail #4: Validation

  1. cramsay says:

    Hi Jason,

    Did the error you were getting in IE7 match the one in that blog post? AFAIK the MR validation uses this library:

    http://www.tetlaw.id.au/view/javascript/really-easy-field-validation

    That in turn uses prototype, which should be able to handle inserting stuff in IE7 with ease. I assume the error was happening when the validation messages were trying to appear?

  2. I did comment on Hammett’s post but can’t find the comment. Did he update the blog entry? Where is it exactly?

    Nice post on validation. I will definitely try it out.

    Yes, the error was appearing when the messages were trying to appear.

  3. Joe Ocampo says:

    Nice post…

    Don’t know how crazy I am about decorating my Domain object with validation constructs BUT I am in simplification rehab for this.

    (mumbling to self) must simplify, it will be ok. must simplify, its ok…..

    :-)

  4. Jason,

    Very detailed post!

    I agree with Joe that I cringe at the idea of decorating domain objects with validation attributes. Although I have done quite a bit of research and thought about it alot without coming up with a better scenario without creating more headaches as a result.

    At one point in a recent project I had a generic ValidationBase controller that the controller derived from and I could then call a Validate method returning errors if not passed. This was for a Controller Wizard, hence the generics.

    I have been thinking about this a lot recently so perhaps I can come up with an alternative.

    Great post, keep it up!

  5. I completely agree with you and Joe, hence my second opening paragraph. I love the idea of a ValidationBase controller.

    I’m check out Colin’s javascript validation stuff and I look forward to posts about your base validation mechanism.

    Excellent!

  6. joeyDotNet says:

    Well I’d agree mostly about not wanting to put a bunch of validation attributes on domain objects. BUT! Like Joe, I too have been applying the “replace unnecessary complexity wth simplification” refactoring to myself for a while now. (Hmm, refactoring *yourself*, smells like a blog post…) :)

    And it’s all the Castle project’s fault when I saw the simplicity of how quickly you could get a decently designed web app up and running using MonoRail + ActiveRecord.

    Now that I’m digging into RoR, it’s like that whole Castle MR + AR experience all over again, but this time it’s like X 10.

    There really is just something beautiful about having lines like this in your domain object:


    validates_presence_of :title
    validates_numericality_of :quantity

    especially when you can constrain them like this:


    validates_presence_of :title :on => :create

    I dunno. It’s definitely starting to grow on me. And I have a feeling that an Active Record implementation in a dynamic language is completely different than in a static language when it comes corrupting your design. But, I’m still learning…

    (Sorry this kinda turned into a AR comment, but is kinda along the same lines…)

  7. deman says:

    Jason,

    I’m waiting for your authentication post. Or did I miss it somewhere?

    Thank you.

  8. Isidro says:

    I’m having troubles with regex validations. If I put any kind of regexes in the validators the validation stops working!

    Is this a know issue?

  9. @ isidro

    Whenever that happens to me, it isn’t the Monorail Validator, it’s my regex. I use Roy Osherove’s Regulator tool to test my regex queries and that usually straightens me out.

    Hope this helps.