Tuesday, 21 August 2007

Form validation with AJAX

Finally managed to utilise javascript and ajax to do client-side form checking. I am using javascript instead of rjs because I feel like I have more control over what I'm doing and also I'm more familiar with the javascript syntax. Since prototype framework is included as part of the javascript defaults in RoR, I will be using that framework too.

Its got some pretty handy things like accessing DOM elements using $('element id'), so you don't have to deal with document.getElementId etc. The framework also provides a cool Ajax object which handles all the nasty readystates that you will come across if you use the conventional way of doing Ajax.

Here is a snippet of the client-side code:

new Ajax.Request("/user/check_exists",
{
parameters: params + "=" + textField.value,
onLoading: showMsg(infoField, "<img src="../../images/ajax/ajax-loader.gif" border="0" /> Checking if available..."),
onSuccess: function(request) {
if (request.responseText)
{
showMsg(infoField, request.responseText);
textField.className = "fail";
textField.focus();
}
else
{
showMsg(infoField, "");
textField.className = "success";
}
}
});
Here is a snippet of the server-side code:

def check_exists
if params[:handle]
@handle_exists = User.find(:first, :conditions => [ "handle = ?", params[:handle] ])
if @handle_exists == nil
render :text => ""
else
render :text => "Username is taken. Please choose another."
end
elsif params[:email]
@email_exists = User.find(:first, :conditions => [ "email = ?", params[:email] ])
if @email_exists == nil
render :text => ""
else
render :text => "This email is registered. Please choose another."
end
end
end
Sorry about the indentations if they don't come out correctly. For some reason this current blogger layout strips any unnecessary whitespaces.

Here are some screen shots of what I've done today. You will notice I used a little CSS to make things look nicer.



[edit date="23-08-07"]

I decided to move the business logic into the model and leave the transaction logic in the controller. More elegant and scalable I reckon.

In the model:
def self.attribute_exists?(attribute, value)
@attribute_exists = User.find(:first, :conditions => [ attribute + " = ?", value ])
if @attribute_exists != nil
return @attribute_exists
else
return nil
end
In the controller:
def check_exists
if params[:actiontype] == "new"
if params[:handle]
@user = User.attribute_exists?("handle", params[:handle])
if @user == nil
render :text => ""
else
render :text => "Username is taken. Please choose another."
end
elsif params[:email]
@user = User.attribute_exists?("email", params[:email])
if @user == nil
render :text => ""
else
render :text => "This email is registered. Please choose another."
end
end
elsif params[:actiontype] = "edit"
@cur_user = User.find(params[:id])

if params[:handle]
@user = User.attribute_exists?("handle", params[:handle])
if @user == nil || @user == @cur_user
render :text => ""
else
render :text => "Username is taken. Please choose another."
end
elsif params[:email]
@user = User.attribute_exists?("email", params[:email])
if @user == nil || @user == @cur_user
render :text => ""
else
render :text => "This email is registered. Please choose another."
end
end
end
end
A general rule of thumb is that code that requires communication with the database should be placed in the model side instead of the controller. The controller's job is to handle the user's request, retrieve data from the model and provide a response if necessary. The view's job is to handle presentation layer (i.e. HTML & CSS) for viewing purposes (thus called the View).

[/edit]

No comments: