Validate single fields trough AJAX with CakePHP
Today i was busy making a form for a signup procedure and i tought: “Lets make it a bit easier for the customer by adding some real-time validation”. Of course this is a piece of cake with.. Cake!
Enabling JavaScript
First we need to load the script.aculo.us libaries.
Download them from script.aculo.us, extract the files and copy prototype.js in /lib to your cake js folder (app/webroot/js) and all the files in /bin to the cake js folder.
Then add the following to your default.ctp (app/views/layouts/default.ctp (if it’s not there create one))
<?php
if(isset($javascript))
{
echo $javascript->link('prototype');
echo $javascript->link('scriptaculous.js');
}
?>
And in your controller (app/controllers/customers_controller.php) load JavaScript (note: you could also put this in app_controller to make it available in every view instad of putting it in every single controller).
var $helpers = array('Html', 'Javascript', 'Form', 'Ajax');
Enabling RequestHanlder
Now we need to make shure we serve the right content with the right request (return xml if it’s requested).
Add the following to your routes file (app/config/routes.php)
Router::parseExtensions();
Now open your controller (app/controllers/customers_controller.php) and add the following line:
var $components = array('RequestHandler');
We can now use the power of requesthandler \0/
Model
Lets make some basic validation rules: (app/models/customer.php)
var $validate = array(
'initials' => array('length' => array('rule' => array('minLength', '1'), 'message'=>'This field cannot be empty')),
'lastname' => array('length' => array('rule' => array('minLength', '3'), 'message'=>'This field cannot be empty'))
);
Now lets make some actions!
Controller
The add action in the controller (app/controllers/customers_controller.php)
function add() {
// Ajax validation of single fields, check if xml is asked
if ($this->RequestHandler->ext =='xml') {
// If we have data, process it. If not send back an error.
if(!empty($this->data['Customer'])){
$this->cleanUpFields();
// Validate the customer, if it's ok, show no errors. If not ok, show errors
if ($this->Customer->create($this->data['Customer']) && $this->Customer->validates()) {
$this->set('error', '0');
$this->set('message', '');
} else {
$errorMessages = $this->validateErrors($this->Customer);
$this->set('error', '1');
$this->set('message', array_shift($errorMessages));
}
} else {
$this->set('error', '1');
$this->set('message', 'No data sent');
}
} else {
// Normal validation and save
if(!empty($this->data)) {
$this->cleanUpFields();
$this->Customer->create($this->data);
if($this->Customer->save($this->data)) {
$this->redirect(array('action'=>'thankyou'));
} else {
$this->Session->setFlash('Woot, something went wrong, please check the red fields!');
}
}
}
}
What we did is check if a xml file was requested by the client (this is what we use in the ajax request), if so, check only a single field and return if there is an error or not.
The XML views
In your customers view folder (app/views/customers) create a new folder named xml. In there create a new file called add.ctp.
This is the file the controller uses when it wants to send back a xml view.
<validation>
<error><?php echo $error; ?></error>
<message><?php echo $message;?></message>
</validation>
We also need to create a layout for the xml view, in the layouts folder there should be a folder named xml (app/views/layouts/xml)
Open it and create a file called default.ctp with the following content
<?php header('Content-type: text/xml'); ?>
<?php echo $content_for_layout; ?>
The add view
Now lets make a form (app/views/customers/add.ctp)
<?php echo $form->create('Customer');?>
<fieldset>
<legend>Name</legend>
<span>
<?php echo $form->input('initials', array('label'=>'Initials', 'maxlength'=>'8', 'size'=>'8', 'class'=>'req', 'div'=>false)); ?>
</span>
<span>
<?php echo $form->input('lastname', array('label'=>'Last name', 'size'=>'20', 'class'=>'req', 'div'=>false)); ?>
</span>
</fieldset>
<?php echo $form->end('Submit'); ?>
This is basically a simple form, but note the ‘class’=>’req’, we are going to use this to let javascript know wich fields to check (this might be handy if you don’t want to validate very single field in the form)
JavaScript
Now the javascript code (place it at the end of app/views/customers/add.ctp)
<script type="text/javascript">
// Look for all req fields and put them in an array
requiredFields = document.getElementsByClassName('req');
// Loop the array and place observers on the req fields
for(i=0; i<requiredFields.length;i++){
new Form.Element.Observer(requiredFields[i], 2, liveActionCallback);
}
// Callback if something happens in one of the req fields
function liveActionCallback( element, value ) {
// Funcion for handling the response XML file wich the Ajax.Request below gets for us
var handlerFunc = function(t) {
var xmlDoc = t.responseXML.documentElement;
// Check if there is an error
if(xmlDoc.getElementsByTagName('error')[0].firstChild.nodeValue == '1'){
// You could do all kinds of stuff here, i'm just adding a classname to the form field wich makes it red
$(element).addClassName('form-error');
} else {
// if there is no error, remove the req class
$(element).removeClassName('req');
}
}
// Create a new ajax request, note the .xml added to the url, it makes shure we get an xml back from the controller.
new Ajax.Request('/regwiz/customer.xml', {
asynchronous:true,
evalScripts:true,
parameters:Form.Element.serialize(element),
onSuccess:handlerFunc
}
);
}
</script>
CSS
As you can see i’m not doing much if there is an error (like not using the error message, but you could of course append a div with the error message, or alert it or… well whatever you want ;)).
To make it complete, here’s the CSS file is used.
/* ----- REQUIRED ----- */
form .req{
border-right: 2px solid red;
}
form .form-error{
display:block !important;
background-color: #FFDFDF !important;
border:1px dotted red;
}
Example
A few images with the different states: (note labels are in Dutch ;))

The field with the .req class (it creates a red right border, that disappears when the field is valid)
A valid field:

And an invalid field:

And the ajax request (from Bugzilla)

And that’s basically it. If you don’t understand everything, please read the comments or try changing stuff :)


13 Comments
comments rss [?] | trackback uri [?]Excellent stuff! Just the kind of thing I was looking for :-)
I use jQuery as my Javascript framework, but the processes is pretty much identical.
Cool to hear! Thanks for the comment :)
Hi, I’m having some trouble trying to make this work.
In the javascript code inside add.ctp, you wrote the ajax request pointing to /regwiz/customer.xml, but I don’t know which xml file should include
/regwiz/customer.xml is the add function but instead of /regwiz/customer you need to add the .xml to let the add function generate xml instead of html.
a normal cake route would be /users/add.xml instead of /users/add
[...] public links >> setflash Validate single fields trough AJAX with CakePHP Saved by JimLovesFirefly on Thu 09-10-2008 Nice tip about using CakePHP setFlash method by Rytis [...]
I only receive the errors after the submit button is clicked. Any suggestions as to what may be going wrong? thanks
Great stuff, had to delete $this->cleanUpFields();
in the controller (using cake1.2) and ajust the url in the javascript. Only thing is that when you delete some input afther the validation the red bar won’t return
“Only thing is that when you delete some input afther the validation the red bar won’t return” Solved…;-) made a mistake
small thing in the java
// if there is no error, remove the req class AND form-error
$(element).removeClassName(’req’);
$(element).removeClassName(’form-error’);
Great tutorial, this is exactly what i was looking for, i just got a problem, I can’t make the validation to do it in “real-time”, if i don’t ´press the sumbit button the validation doesn’t happen, i don’t know what’s missing, i follow all the steps, the only one that confuse me, was the /regwiz/customer.xml part in the script, maybe this is the cause of the problem?
It sounds like a javascript problem, this code is fairly old and the new prototype library may do things different.
This tutorial is realy nice, but I got the same problem ; got to hit submit button so the messages are displayed… I deleted the $this->cleanUpFields(); and changed the path in the javascprit, but it still does’t work. An update would be great !!
For all those who seem to have problems getting this to work, are you sure you have your debug level set to 0?
This got me stumbled and after debugging for a few hours I realized that the SQL dump was breaking the XML response.
Set debug to 0 in your core.php and see if it works (it should).
Submit your comment
XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>