EventValidation: Getting Ajax (AJAJ) client side validation working

Posted At : November 27, 2007 3:16 AM | Posted By : Mark Drew
Related Categories: coldfusion, model-glue, coldspring, EventValidation

One of the downfalls of the current cfform implementation of validation is that it only does it on the client side (as far as I have used it, it might have changed). So, you design your form, add some client side validation, then realise that people without javascript enabled are sending junk and you have to re-implement the validation server side.

So far so good. But then you are asked to change the validation ("hey, check it really is a creditcard number before we send it to the payment gateway", your boss says) and you implement that on the server side, and then copy the functionality to the client side. The list of changes go on, you go make a cup of coffee and forget to implement one side of the validation somewhere.

With EventValidation, you define your validations in one place and you just define whether you are using client side validation. Lets check it out.

Here is our modified form from before, and you notice, there is only one change, adding client="true" to the ev:setup tag (and make sure you import the form tags from "/EventValidator/taglib/form" as well as the showerror="true"):

<h1>Register</h1>
<cfimport prefix="ev" taglib="/EventValidation/taglib">
<cfoutput>
<form action="#ViewState.getValue("myself")#register.action" method="post" >
<ev:setup id="ev_Register" successEvent="register.action" client="true" showerror="true">
<div>
<label for="email">email:</label>
<input type="text" name="email" id="email" value="#ViewState.getValue("email")#">

<label for="password">password:</label>
<input type="password" name="password" id="password" value="#ViewState.getValue("password")#">
</div>
<div>
<input type="submit">
</div>
</form>

What happens when you call this page, the form fields are replaced with a normal formfield and a hidden div. Also, javascript links are added to the header (in this case JQuery, the form plugin and the EventValidator setup script), which setup your form and the AJAJ based calls are added to the forms on the page (of course, it only adds it to forms that need it!) And that is it, your headaches gone.

Apart from one, you would need to make a Virtual Directory on your web server to the "EventValidator/scripts" so you can have access to these scripts, but what if you can't do that (or cant be bothered?). The easiest way to change that is to copy the scripts from EventValidator/scripts somewhere else and change the scriptsrc attribute in the ev:setup tag:

<ev:setup id="ev_Register" successEvent="register.action" client="true" showerror="true" scriptsrc="/scripts/">

Now you are all sorted and can see it working.

One thing that I think I forgot to mention is WHAT you can validate against with the EventValidation Action Pack. I thought I would write up a little guide about what you can validate against and even better, how you can extend the validation object to validate against something that is custom.

The current event data check rules that you can apply are as follows:

Specific EventValidator rules:

  • required: That the field must be in the event and that it must have some length
  • equals: This is a handy function, you can check whether one field equals another, or that a field equals a value, for example to check that the password field matches the password_confirm field we would add this entry in the validation bean:
    <map>
             <entry name="field"><value>password</value></entry>
             <entry name="field2"><value>password_confirm</value></entry>
             <entry name="rule"><value>equals</value></entry>
          </map>
    If of course we wanted to make sure the password was something like "watangy", we could change the field2 to a value, as follows:
    <map>
             <entry name="field"><value>password</value></entry>
             <entry name="value"><value>watangy</value></entry>
             <entry name="rule"><value>equals</value></entry>
          </map>
    Of course, this is not the best thing to do since the error would display:
    "The field #rule.getField()# must equal #rule.getValue()#."
    So, we can override the default error message as follows:
    <map>
                <entry name="field"><value>password</value></entry>
                <entry name="value"><value>watangy</value></entry>
                <entry name="rule"><value>equals</value></entry>
                <entry name="errortext"><value>The password is incorrect</value></entry>
             </map>
  • lessthan/morethan: That the field value is less/more than the value set (such as "you must be over 18 and under 400 to enter this site")
  • maxlength/minlength: The length of the field value is more/less than the value set (such as "your password must be between 40 and 60 characters long")

ColdFusion supplied validation rules: EventValidator uses the isValid function and a lot of the in-built ColdFusion validations, these are (I wont list documentation individually, check the Livedocs for details): boolean,creditcard,date,time,email,eurodate,float,numeric,guid,integer,ssn,social_security_number,string,telephone,url,uuid,usdate,xml,zipcode

Creating your own validations

But what about a validation that needs to contact a database or do some other kind of processing that is specific for your system? Well, these are easy to implement. Lets take an example where a user cannot register with a username that has already been taken, which means that I shall need to go into the database to check this.

First step is to create my own validation component, that extends "EventValidation.model.EventValidator" and overriding the "validateEvent" method, then add a loop through the validation rules and creating my own type of validation, here is a quick example:

<cfcomponent extends="EventValidation.model.EventValidator" output="false">

   <cffunction name="validateEvent" returntype="void" access="public" output="false">
         <cfargument name="event" type="any" required="true">
         <cfargument name="context" type="string" required="true">

         <!--- Call the normal validations --->
         <cfset super(arguments.event, arguments.context)>

         <!--- Loop through the validations --->
         <cfloop from="1" to="#ArrayLen(variables.ValidationMap)#" index="r">
               <!--- Setup defaults for each validation --->
               <cfset rule = CreateObject("component", "Rule").init(variables.ValidationMapr])>
            

            <cfswitch expression="#UCase(rule.getRule())#">
               <!--- our own validation --->
               <cfcase value="usernotexists">

                     <!---
                        Go get the user from the db
                        Of course, this might be a service or interacting with your own ORM
                     --->

                     <cfquery name="getUser">
                        SELECT userid FROM Users WHERE username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#Trim(event.getValue(rule.getField()))#">

                     </cfquery>

                  <cfif getUser.recordcount>
                     <cfset event.getValue("ErrorCollection").addError(rule, "The Username #event.valueExists(rule.getField())# has already been taken")>
                  </cfif>
               </cfcase>
            </cfswitch>


         </cfloop>
   </cffunction>
   </cfcomponent>

Hopefully the comments in the code above explain what is going on, but essentially you are calling super() to let all the previous validations work, and then we loop through the validationMap which contains our entries and create a rule object for each entry, we then use a switch (you dont have to, but I might be extending this to have a whole bunch of user based validations) with an entry for "usernotexists", my new validation rule. I then do a query and check it against the username and if it is found, I add an error to the ErrorCollection.

The next step is to setup my bean, as you would do normally but instead of using "EventValidation.model.EventValidator" I use my extended class:

<bean id="userValidation" class="myProject.model.UserValidator">
          <constructor-arg name="rules">
             <list>
                <map>
                   <entry key="field"><value>username</value></entry>
                   <entry key="rule"><value>usernotexists</value></entry>
                </map>
            </list>
         </constructor-arg>
   </bean>

And there you have it, you have now checked your username against your specific database. Now you can add all sorts of validations that are specific to your own systems!

Event Validation: Showing errors in your page

Posted At : November 26, 2007 10:23 AM | Posted By : Mark Drew
Related Categories: coldfusion, model-glue, coldspring, EventValidation

In the previous post I introduced Event Validation, which on a simple level validates the input from forms in a Model-Glue request against a set of rules that you define in a ColdSpring tag.

Simple so far?

Well, its all very well getting a resultant ErrorCollection back from the server, but you will still need to display the errors, which is another load of work that the Event Validation plugin can help you with. To enable this you simply need to add two things to your view template, another cfimport (apart from the main mv one) and another attribute to your mv:setup tag:

[More]

Introducing EventValidation: Form and Event Validation

Posted At : November 25, 2007 9:32 AM | Posted By : Mark Drew
Related Categories: coldfusion, model-glue, coldspring, EventValidation

EventValidation is another Action Pack for Model-Glue designed towards the validation of forms, but can be used to validate any value within a Model-Glue event.

It provides a way to tag up your form and for it to be validated, both client side and server side, which is something that I find very neat, since I hate repeating CF code in the backend with JavaScript code in the front end.

Let me walk you through a simple installation and setup of EventValidation.

INSTALLATION

Simply extract the EventValidation Action Pack to a secure part of your webserver and create a ColdFusion mapping to "/EventValidation". Then in your model glue file add the following at the top before any other controllers etc:

< include template="/EventValidation/config/EventValidation.xml"/>

You will be defining the rules to validate an event against in EventValidation/config/ColdSpring.xml, so import it into your application's ColdSpring.xml, using a relative path to the resource (I think there is a bug here with ColdSpring but I need to clarify this with Chris Scott) :

<import resource="../../EventValidation/config/ColdSpring.xml" />

The next thing to do, is in the form that you want to validate, is to add an cfimport declaration at the top:

<cfimport prefix="ev" taglib="/EventValidation/taglib">

This will allow you to setup your form with a custom tag as follows:

<h1>Register</h1>
   <cfimport prefix="ev" taglib="/EventValidation/taglib">
   <cfoutput>   
   <form action="#ViewState.getValue("myself")#register.action" method="post" >
      <ev:setup id="ev_Register" successEvent="register.action">
      <div>
       <label for="email">email:</label>
         <input type="text" name="email" id="email" value="#ViewState.getValue("email")#">
      
       <label for="password">password:</label>
         <input type="password" name="password" id="password" value="#ViewState.getValue("password")#">
      </div>
      <div>
         <input type="submit">
      </div>
   </form>

What is happening here? well, using ev:setup we have said that the id if the bean that will do our validation is called ev_Register, and that the success event will be register.action, this form is a normal form, with only the added.

Now, lets define what we need in this form to be validated, in the EventValidation/config/ColdSpring.xml I have the following bean:

<bean id="ev_Register" class="EventValidation.model.EventValidator">
       <constructor-arg name="rules">
          <list>
             <map>
                <entry key="field"><value>email</value></entry>
                <entry key="rule"><value>required</value></entry>
             </map>
             <map>
                <entry key="field"><value>email</value></entry>
                <entry key="rule"><value>email</value></entry>
             </map>
             
             <map>
                <entry key="field"><value>password</value></entry>
                <entry key="rule"><value>required</value></entry>
             </map>
          </list>
       </constructor-arg>
    </bean>
The id matches the id in our ev:setup tag, and then we have a list of maps, or in CF it would be an array of structs, so the first entry says, that the field email is required, then we say that email should match the "email" rule, which is using ColdFusion's internal isValid() list of attributes. Then we do the same thing for the password.

What will happen now when you submit your form, is that an ErrorCollection object will be added to the Event. If there are any errors in the collection, it will return to the event that the form is in and you then do ViewState.getValue("errorCollection").getErrorCollection() to get an array of errors.

So that is a simple way to get started with validation, but this is not the end of what EventValidator can do for you, in the next post I shall examine how to add different styles to the error fields and how to add error descriptions to each element (it does it automagically!)

UPDATE: you can see a simple demo here

EventGuard on Riaforge

Posted At : November 23, 2007 9:28 AM | Posted By : Mark Drew
Related Categories: coldfusion, model-glue, coldspring, EventGuard

Just a quick update that if you are interested in the EventGuard project, want to make some additions/modifications or even if you have found some bugs, I have just checked in the code and posted the zip over at the EventGuard Project page on RiaForge

Any feedback is appreciated!

Introducing EventGuard: Secure your events in Model-Glue

Posted At : November 22, 2007 6:26 PM | Posted By : Mark Drew
Related Categories: coldfusion, model-glue, coldspring, EventGuard

EventGuard is an Action Pack for the Model-Glue framework that allows you to secure your events based whether a user is logged in or using a user's assigned roles.

On a simple level, it allows you to define which event-handler you will use to carry out the login, and then put a list of event-handler names that you want to protect. EventGuard also allows to define which event-handlers you want to exclude in your security check so that you can perform a blanket wide inclusion using "*".

[More]

Model View Controller Song..

Posted At : November 18, 2007 4:49 PM | Posted By : Mark Drew
Related Categories: model-glue, MVC, music

Posted on the CFDEV list this fantastic song about MVC

Check it out (and read the comment at the bottom)

Model Glue Tips: Splitting out ColdSpring

Posted At : October 27, 2007 7:26 PM | Posted By : Mark Drew
Related Categories: coldfusion, model-glue, coldspring

A comment posted in my entry about Model Glue Tips Part 1: Separate out your Model-Glue file reminded me that I should continue with some of those postings.

One of the things that happens when you start building larger applications is that you get a lot of objects that you are managing with ColdSpring. This means that the file can get rather large, and again that is a bad thing right (I keep going on about this to lots of people that write CF, the same goes for your XML files).

There are other reasons to split out the ColdSpring file, apart from a neatness. One of our applications actually creates other ModelGlue applications. And each one of these has separate configurations and in fact available objects (they are automatically written out) as well as maybe some custom configurations.

The problem would be that if you update the core code, that is, objects that are core to the system, you dont want to be messing about with merging these files together, so I tend to split things out something like:

  • ColdSpring.xml   <- Main (Core) ColdSpring File
  • ColdSpring_Config.xml  <- Automatically written file that has our DSN, reactor and other site specific settings
  • ColdSpring_Custom.xml <- Custom settings and mappings for the application
All these files are complete ColdSpring files in themselves (that is, they start with <beans>) and are imported into the main ColdSpring file as imports:

<beans>
    <import requires="config/ColdSpring_Config.xml" />
    <import requires="config/ColdSpring_Custom.xml" />

    ....
    <!-- The rest of the core files -->

   </beans>
Once you have done this, beans in the external files are loaded as normal.

A quick note, and I haven't checked this properly, but this works in the bleeding edge version of ColdSpring which you can get through CVS following the instructions here: http://www.coldspringframework.org/index.cfm?objectid=2DD5F8AF-DD05-F8C8-7860429E21850D7E

Tags: , ,