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):
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:
The layout code performs the following:
-
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.
-
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.
-
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